根据我的理解,在Scala中,函数也可以被调用
传递或 的名字
例如,给定以下声明,我们是否知道函数将如何被调用?
声明:
def f (x:Int, y:Int) = x;
Call
f (1,2)
f (23+55,5)
f (12+3, 44*11)
请问规则是什么?
根据我的理解,在Scala中,函数也可以被调用
传递或 的名字
例如,给定以下声明,我们是否知道函数将如何被调用?
声明:
def f (x:Int, y:Int) = x;
Call
f (1,2)
f (23+55,5)
f (12+3, 44*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))
其他回答
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))
按值调用是常见的用例,这里有很多答案。
名称调用将一个代码块传递给调用方,每次调用时 调用方访问参数,执行代码块,然后 值被计算。
我将尝试用下面的用例以更简单的方式演示按名调用
示例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)
}
下面是我编写的一个快速示例,以帮助我的一位正在学习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
========================
我不认为这里所有的答案都是正确的:
在按值调用中,参数只计算一次:
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,因此节省了一些计算,这有时可能是有益的。
我将尝试通过一个简单的用例来解释,而不仅仅是提供一个示例
想象一下,你想要创建一个“唠叨应用程序”,每当你被唠叨时,它就会唠叨你。
检查以下实现:
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只在通过名称传递时才会工作 原因是,当通过值传递时,它将被重用,因此值将不会被重新计算,而当通过名称传递时,值将在每次访问变量时重新计算