根据我的理解,在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的说法,这是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方法的定义。

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

首先,让我们假设我们有一个带有副作用的函数。这个函数输出一些内容,然后返回一个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())的副作用只发生了一次。然而,在叫名字的版本中,副作用发生了两次。

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

Scala变量计算在better https://sudarshankasar.medium.com/evaluation-rules-in-scala-1ed988776ae8中解释

def main(args: Array[String]): Unit = { //valVarDeclaration 2 println("****starting the app***") // ****starting the app*** val defVarDeclarationCall1 = defVarDeclaration // defVarDeclaration 1 val defVarDeclarationCall2 = defVarDeclaration // defVarDeclaration 1 val valVarDeclarationCall1 = valVarDeclaration // val valVarDeclarationCall2 = valVarDeclaration // val lazyValVarDeclarationCall1 = lazyValVarDeclaration // lazyValVarDeclaration 3 val lazyValVarDeclarationCall2 = lazyValVarDeclaration // callByValue({ println("passing the value "+ 10) 10 }) // passing the value 10 // call by value example // 10 callByName({ println("passing the value "+ 20) 20 }) // call by name example // passing the value 20 // 20 } def defVarDeclaration = { println("defVarDeclaration " + 1) 1 } val valVarDeclaration = { println("valVarDeclaration " + 2) 2 } lazy val lazyValVarDeclaration = { println("lazyValVarDeclaration " + 3) 3 } def callByValue(x: Int): Unit = { println("call by value example ") println(x) } def callByName(x: => Int): Unit = { println("call by name example ") println(x) }

看到这个:

    object NameVsVal extends App {

  def mul(x: Int, y: => Int) : Int = {
    println("mul")
    x * y
  }
  def add(x: Int, y: Int): Int = {
    println("add")
    x + y
  }
  println(mul(3, add(2, 1)))
}

y: => Int是名称调用。通过名称调用传递的是add(2,1)。这将被延迟计算。因此控制台的输出将是“mul”后面跟着“add”,尽管add似乎是先被调用的。按名称调用相当于传递一个函数指针。 现在将y: => Int改为y: Int。控制台将显示“add”后面跟着“mul”!通常的评估方法。

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

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

检查以下实现:

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