1

我正在学习 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 变量每次是如何变化的,所以有人可以解释一下它是如何工作的吗?

4

3 回答 3

1

当您声明具有类型的参数时,=> Type您将该参数声明为匿名函数(一个只返回Type没有任何输入的函数)。因此,当第一次调用该函数时,每个参数都会针对i每次的特定值进行评估。随着每次迭代body更改值,程序将在每次更改时重新评估。iibody

我知道这听起来很复杂,但请耐心等待。让我们看看当你删除=>.

如果您删除了=>,则不会声明要重新评估匿名函数。您正在定义无法重写的参数。而且由于不能每次都重新评估条件,您将拥有无限的bucle。

我希望这个解释能提供一些帮助。

于 2015-05-28T21:54:17.777 回答
1

在这个例子中,身体

println(i)
i -= 1

是一个对主体定义范围内的变量进行操作的闭包。i因此i不是主体的局部变量,这意味着该操作-=修改了唯一存在的 value i,而不是在方法调用后被丢弃的本地副本。

条件也是如此:它是一个捕获相同变量的闭包i,因此在每次执行主体后,条件将看到现在更新的值i

让我们在不改变含义的情况下稍微重写示例:首先,我们可以重写whileLoop以将函数作为参数而不是按名称调用参数:

def whileLoop(cond: () => Boolean)(body: () => Unit): Unit =
  if (cond()) {
    body()
    whileLoop(cond)(body)
  }

这种重写whileLoop在语义上是相同的,因为按名称调用参数作为表达式而不是表达式的求值传递。免责声明:我不知道是否存在技术差异,例如,关于性能。

其次,我们可以使传递的表达式condbody不带参数的函数:

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在评估它们时被访问和修改。

于 2015-05-28T21:54:19.357 回答
1

i -= 1 获取变量 i 并将其重新分配给其减 1 的值。您的 body 引用了相同的 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
于 2015-05-28T21:54:29.637 回答