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

传递或 的名字

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

声明:

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

Call

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

请问规则是什么?


当前回答

下面是Martin Odersky的一个例子:

def test (x:Int, y: Int)= x*x

我们希望检查评估策略,并确定在这些条件下哪个更快(步骤更少):

test (2,3)

按值调用:test(2,3) -> 2*2 -> 4 按名称调用:test(2,3) -> 2*2 -> 4 这里的结果是用相同数量的步骤达到的。

test (3+4,8)

按值调用:test (7,8) -> 7*7 -> 49 按名字呼叫:(3+4)(3+4)-> 7(3+4)-> 7*7 ->49 这里按值调用更快。

test (7,2*4)

按值调用:test(7,8) -> 7*7 -> 49 按名字呼叫:7 * 7 -> 49 在这里叫名字比较快

test (3+4, 2*4) 

按值调用:test(7,2*4) -> test(7,8) -> 7*7 -> 49 按名字呼叫:(3+4)(3+4)-> 7(3+4)-> 7*7 -> 49 结果是在相同的步骤中达到的。

其他回答

下面是Martin Odersky的一个例子:

def test (x:Int, y: Int)= x*x

我们希望检查评估策略,并确定在这些条件下哪个更快(步骤更少):

test (2,3)

按值调用:test(2,3) -> 2*2 -> 4 按名称调用:test(2,3) -> 2*2 -> 4 这里的结果是用相同数量的步骤达到的。

test (3+4,8)

按值调用:test (7,8) -> 7*7 -> 49 按名字呼叫:(3+4)(3+4)-> 7(3+4)-> 7*7 ->49 这里按值调用更快。

test (7,2*4)

按值调用:test(7,8) -> 7*7 -> 49 按名字呼叫:7 * 7 -> 49 在这里叫名字比较快

test (3+4, 2*4) 

按值调用:test(7,2*4) -> test(7,8) -> 7*7 -> 49 按名字呼叫:(3+4)(3+4)-> 7(3+4)-> 7*7 -> 49 结果是在相同的步骤中达到的。

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

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

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只在通过名称传递时才会工作 原因是,当通过值传递时,它将被重用,因此值将不会被重新计算,而当通过名称传递时,值将在每次访问变量时重新计算

我不认为这里所有的答案都是正确的:

在按值调用中,参数只计算一次:

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

// following the substitution model

f(12 + 3, 4 * 11)
f(15, 4194304)
15

你可以在上面看到,不管是否需要,所有的参数都会被求值,通常按值调用可以很快,但并不总是像这种情况。

如果评估策略是按名称调用的,那么分解将是:

f(12 + 3, 4 * 11)
12 + 3
15

正如你所看到的,我们从来不需要计算4 * 11,因此节省了一些计算,这有时可能是有益的。

正如我假设的那样,上面讨论的按值调用函数只是将值传递给函数。根据Martin Odersky的说法,这是Scala遵循的一种评估策略,在函数评估中扮演着重要的角色。但是,让叫名字变得简单。这就像将函数作为参数传递给方法(也称为高阶函数)。当方法访问传递参数的值时,它调用传递函数的实现。如下:

根据@dhg示例,首先创建方法如下:

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

这个函数包含一个println语句并返回一个整数值。创建函数,其参数为call-by-name:

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

此函数参数定义了一个匿名函数,返回一个整数值。在这个x中包含一个函数的定义,它传递0个参数,但返回int值,我们的something函数包含相同的签名。调用函数时,将函数作为参数传递给callByName。但在按值调用的情况下,它只将整数值传递给函数。我们将函数调用如下:

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

在这里,我们的something方法调用了两次,因为当我们在callByName方法中访问x的值时,它调用了something方法的定义。