Scala Lazy动态编程

时间:2018-04-14 20:36:10

标签: scala dynamic-programming lazy-evaluation

所以我正在关注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理解语法等同于第一个代码片段?

1 个答案:

答案 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
}

这会产生相同的预期结果。