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

其他回答

按值调用是常见的用例,这里有很多答案。

名称调用将一个代码块传递给调用方,每次调用时 调用方访问参数,执行代码块,然后 值被计算。

我将尝试用下面的用例以更简单的方式演示按名调用

示例1:

function下面是按名称调用的简单示例/用例,它将function作为参数并给出所经过的时间。

 /**
   * Executes some code block and prints to stdout the 
time taken to execute   the block 
for interactive testing and debugging.
   */
  def time[T](f: => T): T = {
    val start = System.nanoTime()
    val ret = f
    val end = System.nanoTime()

    println(s"Time taken: ${(end - start) / 1000 / 1000} ms")

    ret
  }

示例2:

apache spark(与scala一起)使用按名称调用的方式使用日志记录,参见日志特性 其中其惰性计算是否日志。isInfoEnabled或否。

protected def logInfo(msg: => String) {
     if (log.isInfoEnabled) log.info(msg)
 }

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

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

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

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

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

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

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

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

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

下面是我编写的一个快速示例,以帮助我的一位正在学习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
========================