根据我的理解,在Scala中,函数也可以被调用

传递或 的名字

例如,给定以下声明,我们是否知道函数将如何被调用?

声明:

def  f (x:Int, y:Int) = x;

Call

f (1,2)
f (23+55,5)
f (12+3, 44*11)

请问规则是什么?


当前回答

在按值调用中,表达式的值是在函数调用时预先计算的,并且该特定值作为参数传递给相应的函数。相同的值将在整个函数中使用。

而在名称调用中,表达式本身作为参数传递给函数,并且仅在调用特定参数时在函数内部计算。

Scala中按名称调用和按值调用之间的区别可以通过下面的例子更好地理解:

代码片段

object CallbyExample extends App {

  // function definition of call by value
  def CallbyValue(x: Long): Unit = {
    println("The current system time via CBV: " + x);
    println("The current system time via CBV " + x);
  }

  // function definition of call by name
  def CallbyName(x: => Long): Unit = {
    println("The current system time via CBN: " + x);
    println("The current system time via CBN: " + x);
  }

  // function call
  CallbyValue(System.nanoTime());
  println("\n")
  CallbyName(System.nanoTime());
}

输出

The current system time via CBV: 1153969332591521
The current system time via CBV 1153969332591521


The current system time via CBN: 1153969336749571
The current system time via CBN: 1153969336856589

在上面的代码片段中,对于函数调用CallbyValue(system . nanotime()),系统纳米时间是预先计算的,并且预先计算的值已将参数传递给函数调用。

但是在CallbyName(System.nanoTime())函数调用中,表达式“System.nanoTime())”本身作为参数传递给函数调用,当在函数内部使用该参数时,将计算该表达式的值。

注意CallbyName函数的函数定义,其中有一个=>符号分隔参数x及其数据类型。这里的特定符号表明该函数是按名称调用类型的。

换句话说,按值调用的函数实参在进入函数之前求值一次,而按名称调用的函数实参仅在需要时才在函数内部求值。

希望这能有所帮助!

其他回答

我将尝试通过一个简单的用例来解释,而不仅仅是提供一个示例

想象一下,你想要创建一个“唠叨应用程序”,每当你被唠叨时,它就会唠叨你。

检查以下实现:

object main  {

    def main(args: Array[String]) {

        def onTime(time: Long) {
            while(time != time) println("Time to Nag!")
            println("no nags for you!")
        }

        def onRealtime(time: => Long) {
            while(time != time) println("Realtime Nagging executed!")
        }

        onTime(System.nanoTime())
        onRealtime(System.nanoTime())
    }
}

在上述实现中,nagger只在通过名称传递时才会工作 原因是,当通过值传递时,它将被重用,因此值将不会被重新计算,而当通过名称传递时,值将在每次访问变量时重新计算

CallByName在使用时被调用,callByValue在遇到语句时被调用。

例如:-

我有一个无限循环,即如果你执行这个函数,我们将永远不会得到scala提示。

scala> def loop(x:Int) :Int = loop(x-1)
loop: (x: Int)Int

callByName函数接受上述循环方法作为参数,并且从不在函数体中使用。

scala> def callByName(x:Int,y: => Int)=x
callByName: (x: Int, y: => Int)Int

在callByName方法的执行中,我们没有发现任何问题(我们得到scala提示返回),因为我们没有在callByName函数中使用循环函数。

scala> callByName(1,loop(10))
res1: Int = 1
scala> 

callByValue函数将上述循环方法作为参数,因为函数内部的结果或表达式在执行外部函数之前被递归执行的循环函数求值,并且我们永远不会得到scala提示。

scala> def callByValue(x:Int,y:Int) = x
callByValue: (x: Int, y: Int)Int

scala> callByValue(1,loop(1))

您给出的示例只使用了按值调用,因此我将给出一个新的、更简单的示例来显示两者的区别。

首先,让我们假设我们有一个带有副作用的函数。这个函数输出一些内容,然后返回一个Int型。

def something() = {
  println("calling something")
  1 // return value
}

现在我们将定义两个函数,它们接受完全相同的Int参数,除了一个以值调用风格(x: Int)接受参数,另一个以名称调用风格(x: => Int)接受参数。

def callByValue(x: Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

当我们用副作用函数调用它们时会发生什么?

scala> callByValue(something())
calling something
x1=1
x2=1

scala> callByName(something())
calling something
x1=1
calling something
x2=1

因此,您可以看到,在按值调用版本中,传入函数调用(something())的副作用只发生了一次。然而,在叫名字的版本中,副作用发生了两次。

这是因为按值调用函数在调用函数之前会计算传入表达式的值,因此每次都会访问相同的值。相反,每次访问传入表达式时,名称调用函数都会重新计算传入表达式的值。

参数通常是按值传递的,这意味着它们在被替换到函数体之前会被求值。

在定义函数时,可以使用双箭头强制按名称调用参数。

// first parameter will be call by value, second call by name, using `=>`
def returnOne(x: Int, y: => Int): Int = 1

// to demonstrate the benefits of call by name, create an infinite recursion
def loop(x: Int): Int = loop(x)

// will return one, since `loop(2)` is passed by name so no evaluated
returnOne(2, loop(2))

// will not terminate, since loop(2) will evaluate. 
returnOne(loop(2), 2) // -> returnOne(loop(2), 2) -> returnOne(loop(2), 2) -> ... 

下面是我编写的一个快速示例,以帮助我的一位正在学习Scala课程的同事。我觉得有趣的是,Martin没有使用之前在讲座中提到的&&问题的答案作为例子。无论如何,我希望这能有所帮助。

val start = Instant.now().toEpochMilli

val calc = (x: Boolean) => {
    Thread.sleep(3000)
    x
}


def callByValue(x: Boolean, y: Boolean): Boolean = {
    if (!x) x else y
}

def callByName(x: Boolean, y: => Boolean): Boolean = {
    if (!x) x else y
}

new Thread(() => {
    println("========================")
    println("Call by Value " + callByValue(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


new Thread(() => {
    println("========================")
    println("Call by Name " + callByName(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


Thread.sleep(5000)

代码的输出如下:

========================
Call by Name false
Time 64ms
========================
Call by Value false
Time 3068ms
========================