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

传递或 的名字

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

声明:

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

Call

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

请问规则是什么?


当前回答

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

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

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,因此节省了一些计算,这有时可能是有益的。

其他回答

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

通过一个例子可以帮助您更好地理解其中的区别。

让我们定义一个返回当前时间的简单函数:

def getTime = System.currentTimeMillis

现在我们将定义一个函数,通过名称,打印两次延迟一秒的时间:

def getTimeByName(f: => Long) = { println(f); Thread.sleep(1000); println(f)}

一个值为1:

def getTimeByValue(f: Long) = { println(f); Thread.sleep(1000); println(f)}

现在让我们分别调用:

getTimeByName(getTime)
// prints:
// 1514451008323
// 1514451009325

getTimeByValue(getTime)
// prints:
// 1514451024846
// 1514451024846

结果应该可以解释这种差异。这个代码片段可以在这里找到。

为了重复@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

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

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

这样,在函数中调用参数x之前,将不会计算参数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 结果是在相同的步骤中达到的。