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

传递或 的名字

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

声明:

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

Call

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

请问规则是什么?


当前回答

为了重复@Ben在上面评论中的观点,我认为最好将“按名字称呼”视为语法糖。解析器只是将表达式包装在匿名函数中,以便稍后在使用它们时调用它们。

实际上,不是定义

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

并运行:

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

你也可以这样写:

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

并运行它,以达到同样的效果:

callAlsoByName(() => {something()})

calling something
x1=1
calling something
x2=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())的副作用只发生了一次。然而,在叫名字的版本中,副作用发生了两次。

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

在你的例子中,所有的参数都将在函数中调用之前被求值,因为你只是通过值定义它们。 如果你想通过名称定义参数,你应该传递一个代码块:

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

这样,在函数中调用参数x之前,将不会计算参数x。

这篇小文章也很好地解释了这一点。

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))

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) }

下面是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 结果是在相同的步骤中达到的。