【问题标题】:Dynamically changing functions scala动态变化的函数
【发布时间】:2015-08-11 14:02:36
【问题描述】:

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

【问题讨论】:

    标签: scala recursion dsl callbyname


    【解决方案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 在语义上是相同的,因为按名称调用的参数作为表达式而不是表达式的求值传递。免责声明:我不知道是否存在技术差异,例如关于性能。

    其次,我们可以为 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 可以从condDefbodyDef 访问,并且在评估它们时访问和修改。

    【讨论】:

    • 那么看起来超出主体和条件范围的外部值 i 仍然在主体内部引用?这在技术上是如何实现的?
    • @Juru i 处于身体和状态两者的周围环境中。我更新了答案,试图使功能更明确。关于闭包的技术实现,我建议阅读 Wikipediathis question on SO
    【解决方案2】:

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

    我知道这听起来很复杂,但请耐心等待。让我们看看删除 => 后会发生什么。

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

    我希望这个解释能给你一些帮助。

    【讨论】:

      【解决方案3】:

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-02
        • 2019-04-10
        • 1970-01-01
        • 2018-07-19
        • 2020-06-26
        相关资源
        最近更新 更多