动态更改功能scala

时间:2015-05-28 21:08:36

标签: scala recursion dsl callbyname

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

3 个答案:

答案 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参数作为表达式而不是表达式的求值传递。免责声明:我不知道是否存在技术差异,例如性能差异。

其次,我们可以为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
  }
}

因此,icondDef都可以访问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