根据我的理解,在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)
请问规则是什么?
当前回答
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 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只在通过名称传递时才会工作 原因是,当通过值传递时,它将被重用,因此值将不会被重新计算,而当通过名称传递时,值将在每次访问变量时重新计算
您给出的示例只使用了按值调用,因此我将给出一个新的、更简单的示例来显示两者的区别。
首先,让我们假设我们有一个带有副作用的函数。这个函数输出一些内容,然后返回一个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课程的同事。我觉得有趣的是,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
========================
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) }
我不认为这里所有的答案都是正确的:
在按值调用中,参数只计算一次:
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,因此节省了一些计算,这有时可能是有益的。