gpt4 book ai didi

scala - 如何在递归上下文中解释惰性?

转载 作者:行者123 更新时间:2023-12-04 19:39:49 25 4
gpt4 key购买 nike

这是来自 FPIS 的代码

object test2 {

//a naive IO monad
sealed trait IO[A] { self =>
def run: A
def map[B](f: A => B): IO[B] = new IO[B] { def run = f(self.run) }
def flatMap[B](f: A => IO[B]): IO[B] = {
println("calling IO.flatMap")
new IO[B] {
def run = {
println("calling run from flatMap result")
f(self.run).run
}
}
}
}

object IO {
def unit[A](a: => A): IO[A] = new IO[A] { def run = a }
def apply[A](a: => A): IO[A] = unit(a) // syntax for IO { .. }
}

//composer in question
def forever[A,B](a: IO[A]): IO[B] = {
lazy val t: IO[B] = a flatMap (_ => t)
t
}

def PrintLine(msg: String) = IO { println(msg) }

def say = forever(PrintLine("Still Going..")).run
}

test2.say 将在堆栈溢出之前打印数千个“Still Going”。但我不知道这到底是怎么发生的。

输出如下所示:
Scala> test2.say
调用 IO.flatMap //只有一次
从 flatMap 结果调用 run
仍在继续..
从 flatMap 结果调用 run
仍在继续..

...//重复直到堆栈溢出

当函数永远返回时,惰性 val t 是否完全计算(缓存)?
而且, flatMap 方法似乎只被调用一次(我添加了打印语句),这与永远的递归定义相悖。为什么?

============
我觉得有趣的另一件事是,forever[A, B] 中的 B 类型可以是任何东西。 Scala 实际上可以在不透明的情况下运行。

我手动尝试过永远[单位,双人],永远[单位,字符串]等,一切都奏效了。这感觉很聪明。

最佳答案

什么 forever方法确实是,顾名思义,使 monadic 实例 a永远运行。更准确地说,它为我们提供了无限的一元操作链。

它的值(value) t递归定义为:

t = a flatMap (_ => t)

扩展到
t = a flatMap (_ => a flatMap (_ => t))

扩展到
t = a flatMap (_ => a flatMap (_ => a flatMap (_ => t)))

等等。
Lazy使我们能够定义这样的东西。如果我们删除了惰性部分,我们会得到一个“前向引用”错误(如果递归值包含在某个方法中),或者它会简单地用默认值初始化而不是递归使用(如果包含在一个类中,使其成为具有幕后 getter 和 setter 的类字段)。

演示:
val rec: Int = 1 + rec
println(rec) // prints 1, "rec" in the body is initialized to default value 0


def foo() = {
val rec: Int = 1 + rec // ERROR: forward reference extends over definition of value rec
println(rec)
}

但是,仅此而已并不是整个堆栈溢出事件发生的原因。有 另一个递归部分 ,而这个其实是栈溢出的原因。它隐藏在这里:
def run = {
println("calling run from flatMap result")
f(self.run).run
}

方法 run调用自身(参见 self.run )。当我们这样定义它时,我们不评估 self.run现场原因 f还没有被调用;我们只是说明一旦调用 run() 就会调用它。

但是当我们创建值 tforever ,我们正在创建一个 IO monad,它可以将 flatMaps 集成到自身中(它提供给 flatMap 的功能是“评估自己”)。这将触发 run因此评估和调用 f .我们从来没有真正离开过 flatMap 上下文(因此对于 flatMap 部分只有一个打印语句),因为一旦我们尝试 flatMap, run开始评估函数 f,它返回我们调用 run 的 IO,它调用函数 f,它返回我们调用的 IO,run 调用函数 f,它返回我们调用 run 的 IO...

关于scala - 如何在递归上下文中解释惰性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43487391/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com