【问题标题】:Scala Lazy Dynamic ProgrammingScala 惰性动态规划
【发布时间】:2018-04-14 20:36:09
【问题描述】:

所以我正在关注http://jelv.is/blog/Lazy-Dynamic-Programming/ 并在 Scala 中实现斐波那契示例。这是我的实现:

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的理解语法相当于第一个代码sn-p?

【问题讨论】:

    标签: scala dynamic-programming lazy-evaluation


    【解决方案1】:

    你是对的,“fibs(i - 1) 计算得太早了”。当你调用doFib 时它会立即被评估,因为doFib(i) 需要fibs(i - 1) 才能返回任何东西,而这又需要doFib(i - 1) 的返回值等等,这样递归就完全展开了在构造惰性整数数组时(在调用 doFib(n).value 之前)。

    如果你想让它变得懒惰,那么返回一个不需要立即评估 fibs(i - 1)Lazy

    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
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-15
      • 1970-01-01
      • 2019-01-14
      相关资源
      最近更新 更多