我正在尝试通过回忆在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
函数添加备忘录注释的通用方法
答案 0 :(得分:3)
获得斐波那契数列的一种方法是通过递归Stream
。
val fib: Stream[BigInt] = 0 #:: fib.scan(1:BigInt)(_+_)
流的一个有趣的方面是,如果流的顶部保留了某些内容,则会自动存储计算结果。因此,在这种情况下,由于标识符fib
是val
而不是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
方法转换为函数,因此您可以将def
与def
包装在一起,但无法从中获取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))
是一个值,但调用fib
,fib_mem_val
和fib_mem_def
的语法没有什么区别。您也可以尝试以下示例online
注意:请注意,某些ScalaZ fib_mem_val
实现不是线程安全的。
对于Memo
部分来说,任何lazy
的好处都是典型的:具有底层存储的实际值只有在第一次使用时才会创建。如果仍然使用该方法,则将其声明为lazy val