所以我正在关注http://jelv.is/blog/Lazy-Dynamic-Programming/并在Scala中实现Fibonacci示例。这是我的实施:
class Lazy[T] (expr : => T) {
lazy val value = expr
def apply(): T = value
}
object Lazy{ def apply[T](expr : => T) = new Lazy({expr}) }
def fib(n: Int): Int = {
def doFib(i: Int): Lazy[Int] = Lazy {
if (i <= 2) 1
else fibs(i - 1)() + fibs(i - 2)()
}
lazy val fibs = Array.tabulate[Lazy[Int]](n)(doFib)
doFib(n).value
}
fib(5)
在这种情况下,fib(5)
正确返回结果5。
然后我想看看是否可以通过尝试以下代码使Lazy[T]
成为monad,这会导致StackOverflow运行时错误:
class Lazy[T] (expr : => T) {
lazy val value = expr
def apply(): T = value
def flatMap[A](f: T => Lazy[A]): Lazy[A] = Lazy { f(value).value }
def map[A](f: T => A): Lazy[A] = Lazy { f(value) }
}
object Lazy{ def apply[T](expr : => T) = new Lazy({expr}) }
def fib(n: Int): Int = {
def doFib(i: Int): Lazy[Int] =
if (i <= 2) Lazy(1)
else for {
a <- fibs(i - 1)
b <- fibs(i - 2)
} yield a + b
lazy val fibs = Array.tabulate[Lazy[Int]](n)(doFib)
doFib(n).value
}
fib(5)
似乎过早地计算了fibs(i-1),这导致无限递归。我想知道是否有for
理解语法等同于第一个代码片段?
答案 0 :(得分:3)
你是对的,&#34; fibs(i - 1)计算得太早&#34;。当您致电doFib
时会立即对其进行评估,因为doFib(i)
需要fibs(i - 1)
才能返回任何内容,而这又需要doFib(i - 1)
的返回值等等on,以便在构造惰性int数组时(在调用doFib(n).value
之前)递归完全展开。
如果你想要它懒惰,那么返回Lazy
,不需要立即评估fibs(i - 1)
:
class Lazy[T] (expr : => T) {
lazy val value = expr
def apply(): T = value
def flatMap[A](f: T => Lazy[A]): Lazy[A] = Lazy { f(value).value }
def map[A](f: T => A): Lazy[A] = Lazy { f(value) }
}
object Lazy{ def apply[T](expr : => T) = new Lazy({expr}) }
def fib(n: Int): Int = {
def doFib(i: Int): Lazy[Int] =
if (i <= 2) Lazy(1)
else Lazy{ (for {
a <- fibs(i - 1)
b <- fibs(i - 2)
} yield a + b).value
}
lazy val fibs = Array.tabulate[Lazy[Int]](n)(doFib)
doFib(n).value
}
println(fib(40)) // 102334155
或者,您可以将整个if-else
打包在Lazy
:
def doFib(i: Int): Lazy[Int] = Lazy {
if (i <= 2) 1
else (for {
a <- fibs(i - 1)
b <- fibs(i - 2)
} yield a + b).value
}
这会产生相同的预期结果。