【问题标题】:Functional Programming: Having a very difficult time understanding how this function lazily evaluates函数式编程:很难理解这个函数是如何惰性求值的
【发布时间】:2020-02-19 04:30:04
【问题描述】:

我正在阅读Functional Programming in Scala 这本书,并且正在阅读关于惰性评估的部分。有一个练习可以使用foldRight 实现takeWhile 函数。我能够成功完成它,但是当我添加打印语句时,我看到它似乎正在处理我没想到的。我对此感到非常困惑。

代码如下:

trait McStream[+A] {
    def uncons: Option[(A, McStream[A])]

    def isEmpty: Boolean = uncons.isEmpty

    def toList: List[A] = {
      uncons match {
        case Some(head -> tail) => head :: tail.toList
        case None => Nil
      }
    }

    def foldRight[B](z: => B)(f: (A, => B) => B): B = {
      uncons match {
        case Some(head -> tail) =>
          println(s"Inside foldRight, head is $head")
          f(head, tail.foldRight(z)(f))
        case None => z
      }
    }

    // TODO how does evaluate?  Trace steps
    // TODO it seems to be storing a deferred takeWhile in the `b` variable that evaluates during the cons
    def takeWhile(p: A => Boolean): McStream[A] = {
      foldRight(McStream.empty[A]) { (a, b) =>
        println(s"a is $a, b is $b")
        if (p(a)) {
          McStream.cons(a, b)
        } else {
          McStream.empty
        }
      }
    }
}

使用构造函数的辅助对象:

object McStream {
    def empty[A]: McStream[A] = new McStream[A] {
      override def uncons: Option[(A, McStream[A])] = None
    }

    def cons[A](hd: => A, tl: => McStream[A]): McStream[A] = {
      new McStream[A] {
        lazy val uncons = Some((hd, tl))
      }
    }

    def apply[A](as: A*): McStream[A] = {
      if (as.isEmpty) empty
      else cons(as.head, apply(as.tail: _*))
    }
  }
}

这是我正在运行的测试:

"take while a predicate is matched" in {
      val stream = McStream(1, 2)
      stream.takeWhile(_ < 2).toList shouldEqual List(1)
    }

这是我得到的输出:

Inside foldRight, head is 1
Inside foldRight, head is 2
a is 2, b is McStream(None)
a is 1, b is McStream(None)
Inside foldRight, head is 2
a is 2, b is McStream(None)

我对最后两行感到困惑,对我来说,它似乎应该一直递归到列表的末尾,然后如果谓词匹配,则将当前处理的尾部连接到下一个元素,或者替换否则为空的McStream。此时,它应该只是返回列表,而不是进行额外的foldRight 和评估。

据我所知,这是评估顺序:

Stream(1, Stream(2, Stream.empty)).takeWhile(_ < 2)
should print Inside foldRight, head is 1
Stream(2, Stream.empty).takeWhile(_ < 2)
should print Inside foldRight, head is 2
Stream.empty.takeWhile(_ < 2)
End of recursion, starts to return
Stream(2, Stream.empty).takeWhile(_ < 2)
should print a is 2, b is Stream.empty
Stream(1, Stream.empty).takeWhile(_ < 2)
should print a is 1, b is Stream.empty
1 #:: Stream.empty

提前致谢!

【问题讨论】:

    标签: scala functional-programming


    【解决方案1】:

    事实证明,我用来尝试理解评估的机制(println 语句)正在强制评估并导致上述问题。当我删除打印语句时,它的评估结果应该如此。

    不要在你的懒惰评估中使用println

    【讨论】:

      猜你喜欢
      • 2017-05-12
      • 2014-11-09
      • 2021-12-29
      • 2012-09-28
      • 1970-01-01
      • 2012-11-02
      • 2021-03-23
      • 2021-01-13
      • 1970-01-01
      相关资源
      最近更新 更多