我正在学习scala,但我遇到了以下代码。
def whileLoop(cond: => Boolean)(body: => Unit): Unit =
if (cond) {
body
whileLoop(cond)(body)
}
var i = 10
whileLoop (i > 0) {
println(i)
i -= 1
}
输出数字为10到1。
因此,cond和body都是“按名称调用”参数。这意味着它们在函数中使用时会被评估。如果我理解正确的话。我不明白的是身体是怎样的
println(i)
i -= 1
随着变量i的变化,身体所应用的每个递归级别的变化都在变化。但这究竟是如何运作的呢?每次传递相同的函数体时,对我来说这个函数保持不变,但运行程序会显示我。我知道每次都会对这个函数进行求值,但是我不明白i变量里面每次都在变化,所以有人可以解释一下它是如何工作的吗?
答案 0 :(得分:1)
当您声明类型为=> Type
的参数时,您将该参数声明为匿名函数(仅在没有任何输入的情况下返回Type
的函数)。
因此,当第一次调用该函数时,每次都会针对该特定值i
评估每个参数。
由于body
会在每次迭代时更改i
值,因此每次i
更改body
时,程序都会重新评估=>
。
我知道这听起来很复杂,但请耐心等待。让我们看看删除=>
时会发生什么。
如果删除{{1}},则表示您没有声明要重新评估的匿名函数。您正在定义无法重写的参数。由于每次都无法重新评估条件,你将拥有无限的支持。
我希望这个解释可以提供一些帮助。
答案 1 :(得分:1)
在这个例子中,正文
println(i)
i -= 1
是closure,它对变量i
进行操作,该变量位于正文定义的范围内。因此i
不是正文的局部变量,这意味着操作-=
修改了唯一的现有值i
,而不是在方法调用后被丢弃的本地副本。
对于条件也是如此:它是一个捕获相同变量i
的闭包,因此在每次执行主体之后,条件将看到现在更新的i
值。
让我们稍微重写一下这个例子而不改变其含义:首先,我们可以重写whileLoop
来将函数作为参数而不是逐个调用参数:
def whileLoop(cond: () => Boolean)(body: () => Unit): Unit =
if (cond()) {
body()
whileLoop(cond)(body)
}
这个重写的whileLoop
在语义上是相同的,因为call-by-name参数作为表达式而不是表达式的求值传递。免责声明:我不知道是否存在技术差异,例如性能差异。
其次,我们可以为cond
和body
函数传递不带参数的表达式:
val condDef = () => i > 0
val bodyDef = () => {
println(i)
i -= 1
}
因为它们都引用变量i
,它既不是参数的一部分,也不是在它们的体内定义,我们必须将i
放在它们的范围内。
def main(args: Array[String]) {
var i = 10
val condDef = () => i > 0
val bodyDef = () => {
println(i)
i -= 1
}
whileLoop (condDef) {
bodyDef
}
}
因此,i
和condDef
都可以访问bodyDef
,并在评估它们时进行访问和修改。
答案 2 :(得分:1)
i - = 1接受变量i并将其重新分配给其减1的值。 你的身体正在引用相同的i变量,每次调用body时都会对其进行修改。 忽略所有的递归和你的whileLoop基本上是这样做的:
var i = 10
println(i) // prints 10
i -= 1
println(i) // prints 9
i -= 1
...
i -= 1
println(i) // prints 1
i -= 1
println(i) // prints 0