使用Memo.mutableHashMapMemo在Scala中进行斐波那契记忆

时间:2019-01-01 13:09:13

标签: scala fibonacci scalaz memoization

我正在尝试通过回忆在Scala中实现fibonacci函数

此处给出的一个示例使用case语句: Is there a generic way to memoize in Scala?

import scalaz.Memo
lazy val fib: Int => BigInt = Memo.mutableHashMapMemo {
  case 0 => 0
  case 1 => 1
  case n => fib(n-2) + fib(n-1)
}

似乎变量n被隐式定义为第一个参数,但是如果我将n替换为_,则会出现编译错误

lazy关键字在这里还有什么优势,因为无论是否使用此关键字,该功能似乎都能很好地工作。

但是我想通过适当的输入将其概括为更通用的函数定义

import scalaz.Memo
def fibonachi(n: Int) : Int = Memo.mutableHashMapMemo[Int, Int] {
    var value : Int = 0
    if( n <= 1 ) { value = n; }
    else         { value = fibonachi(n-1) + fibonachi(n-2) }
    return value
}

但是出现以下编译错误

cmd10.sc:4: type mismatch;
 found   : Int => Int
 required: Int
def fibonachi(n: Int) : Int = Memo.mutableHashMapMemo[Int, Int] {
                                                                         ^Compilation Failed
Compilation Failed

因此,我试图理解向scala def函数添加备忘录注释的通用方法

2 个答案:

答案 0 :(得分:3)

获得斐波那契数列的一种方法是通过递归Stream

val fib: Stream[BigInt] = 0 #:: fib.scan(1:BigInt)(_+_)

流的一个有趣的方面是,如果流的顶部保留了某些内容,则会自动存储计算结果。因此,在这种情况下,由于标识符fibval而不是def,因此fib(n)的值仅计算一次,之后就可以简单地获取。

但是,索引Stream仍然是线性操作。如果您想记住这些,可以创建一个简单的备忘录包装器。

def memo[A,R](f: A=>R): A=>R =
  new collection.mutable.WeakHashMap[A,R] {
    override def apply(a: A) = getOrElseUpdate(a,f(a))
  }

val fib: Stream[BigInt] = 0 #:: fib.scan(1:BigInt)(_+_)
val mfib = memo(fib)
mfib(99)  //res0: BigInt = 218922995834555169026

答案 1 :(得分:1)

  

我要问的更笼统的问题是如何采用预先存在的def函数并向其内联添加可变/不可变的备注注释/包装器。

不幸的是,在Scala中无法做到这一点,除非您愿意为此使用macro annotation,这对我来说似乎是过大的,或者使用一些非常丑陋的设计。

矛盾的要求是“ def”和“内联”。这样做的根本原因是,与def内联的任何操作都不能创建任何新位置来存储已存储的值(除非您使用可以重写代码的宏来引入新的val / var个)。您可以尝试使用一些全局缓存来解决此问题,但是此恕我直言属于“丑陋的设计”分支。

ScalaZ Memo的设计用于创建类型为val的{​​{1}},通常在Scala中将其写为Function[K,V]而不是K => V 。这样,产生的def也可以包含缓存值的存储。另一方面,从语法上讲,val方法和def函数的用法之间的差异很小,因此效果很好。由于Scala编译器知道如何将K => V方法转换为函数,因此您可以将defdef包装在一起,但无法从中获取Memo。如果出于某种原因仍然需要def,则需要另一个包装器def

def

请注意,尽管import scalaz.Memo object Fib { def fib(n: Int): BigInt = n match { case 0 => BigInt(0) case 1 => BigInt(1) case _ => fib(n - 2) + fib(n - 1) } // "fib _" converts a method into a function. Sometimes "_" might be omitted // and compiler can imply it but sometimes the compiler needs this explicit hint lazy val fib_mem_val: Int => BigInt = Memo.mutableHashMapMemo(fib _) def fib_mem_def(n: Int): BigInt = fib_mem_val(n) } println(Fib.fib(5)) println(Fib.fib_mem_val(5)) println(Fib.fib_mem_def(5)) 是一个值,但调用fibfib_mem_valfib_mem_def的语法没有什么区别。您也可以尝试以下示例online

注意:请注意,某些ScalaZ fib_mem_val实现不是线程安全的。

对于Memo部分来说,任何lazy的好处都是典型的:具有底层存储的实际值只有在第一次使用时才会创建。如果仍然使用该方法,则将其声明为lazy val

不会带来任何好处