Scala中带有Memo.mutableHashMapMemo的斐波那契记忆

Scala中带有Memo.mutableHashMapMemo的斐波那契记忆,scala,fibonacci,scalaz,memoization,Scala,Fibonacci,Scalaz,Memoization,我正在用记忆法在Scala中实现斐波那契函数 这里给出的一个示例使用了一个case语句: 变量n似乎被隐式定义为第一个参数,但如果我将n替换为\uu 还有,lazy关键字在这里有什么好处,因为该函数在使用和不使用该关键字的情况下似乎都能正常工作 然而,我想用适当的类型将其推广到更通用的函数定义 import scalaz.Memo def fibonachi(n: Int) : Int = Memo.mutableHashMapMemo[Int, Int] { var value :

我正在用记忆法在Scala中实现斐波那契函数

这里给出的一个示例使用了一个case语句:

变量
n
似乎被隐式定义为第一个参数,但如果我将
n
替换为
\uu

还有,
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
}

因此,我试图理解向scala
def
函数添加记忆注释的一般方法,实现斐波那契序列的一种方法是通过递归

val fib: Stream[BigInt] = 0 #:: fib.scan(1:BigInt)(_+_)
流的一个有趣的方面是,如果有东西抓住了流的头部,计算结果会自动记忆。因此,在这种情况下,由于标识符
fib
val
而不是
def
,因此
fib(n)
的值只计算一次,然后简单地检索

然而,为
编制索引仍然是一种线性操作。如果你想把它记下来,你可以创建一个简单的备忘录包装器

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

实现斐波那契序列的一种方法是通过递归

val fib: Stream[BigInt] = 0 #:: fib.scan(1:BigInt)(_+_)
流的一个有趣的方面是,如果有东西抓住了流的头部,计算结果会自动记忆。因此,在这种情况下,由于标识符
fib
val
而不是
def
,因此
fib(n)
的值只计算一次,然后简单地检索

然而,为
编制索引仍然是一种线性操作。如果你想把它记下来,你可以创建一个简单的备忘录包装器

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

我想问的更一般的问题是,如何获取一个预先存在的
def
函数,并在其中内联添加一个可变/不可变的备忘录注释/包装器

不幸的是,在Scala中没有办法做到这一点,除非你愿意为此使用一个对我来说像是过度杀戮的工具,或者使用一些非常丑陋的设计

相互矛盾的要求是“
def
”和“inline”。这样做的根本原因是,无论您在
def
中执行什么操作,都无法创建任何新位置来存储已存储的值(除非您使用的宏可以重新编写引入新
val
/
var
s的代码)。您可以尝试使用一些全局缓存来解决这个问题,但是这个IMHO属于“丑陋的设计”分支

ScalaZ
Memo
的设计用于创建类型为
Function[K,V]
val
,该类型通常用Scala编写为
K=>V
,而不是
def
。这样,生成的
val
也可以包含缓存值的存储。另一方面,在语法上,使用
def
方法和
K=>V
函数之间的差异很小,因此这非常有效。由于Scala编译器知道如何将
def
方法转换为函数,因此可以使用
Memo
包装
def
,但无法从中获取
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))
注意调用
fib
fib_mem_val
fib_mem_def
的语法没有区别,尽管
fib_mem_val
是一个值。你也可以试试这个例子

注意:注意一些ScalaZ
Memo
实现不是线程安全的

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


我想问的更一般的问题是,如何获取一个预先存在的
def
函数,并在其中内联添加一个可变/不可变的备忘录注释/包装器

不幸的是,在Scala中没有办法做到这一点,除非你愿意为此使用一个对我来说像是过度杀戮的工具,或者使用一些非常丑陋的设计

相互矛盾的要求是“
def
”和“inline”。这样做的根本原因是,无论您在
def
中执行什么操作,都无法创建任何新位置来存储已存储的值(除非您使用的宏可以重新编写引入新
val
/
var
s的代码)。您可以尝试使用一些全局缓存来解决这个问题,但是这个IMHO属于“丑陋的设计”分支

ScalaZ
Memo
的设计用于创建类型为
Function[K,V]
val
,该类型通常用Scala编写为
K=>V
,而不是
def
。这样,生成的
val
也可以包含缓存值的存储。另一方面,在语法上,使用
def
方法和
K=>V
函数之间的差异很小,因此这非常有效。由于Scala编译器知道如何将
def
方法转换为函数,因此可以使用
Memo
包装
def
,但无法从中获取
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))
注意调用
fib
fib_mem_val
fib_mem_def
的语法没有区别,尽管
fib_mem_val
是一个值。你也可以试试这个例子

注意:注意一些ScalaZ
Memo
实现不是线程安全的

对于
lazy
部分,好处对于任何
lazy val
都是典型的:实际值为