斐波那契
Elm 中斐波那契的正常递归定义是:
fib1 n = if n <= 1 then n else fib1 (n-2) + fib1 (n-1)
缓存
如果你想要简单的缓存,maxsnew/lazy 库应该可以工作。它使用原生 JavaScript 代码中的一些副作用来缓存计算结果。它经过审查以检查本机代码不会向 Elm 用户暴露副作用,对于记忆,很容易检查它是否保留了程序的语义。
你应该小心你如何使用这个库。当您创建一个Lazy
值时,第一次强制它需要时间,从那时起它就会被缓存。但是,如果您Lazy
多次重新创建该值,则它们不会共享缓存。例如,这不起作用:
fib2 n = Lazy.lazy (\() ->
if n <= 1
then n
else Lazy.force (fib2 (n-2)) + Lazy.force (fib2 (n-1)))
工作解决方案
我通常看到用于斐波那契的是一个惰性列表。我将给出整个编译代码:
import Lazy exposing (Lazy)
import Debug
-- slow
fib1 n = if n <= 1 then n else fib1 (n-2) + fib1 (n-1)
-- still just as slow
fib2 n = Lazy.lazy <| \() -> if n <= 1 then n else Lazy.force (fib2 (n-2)) + Lazy.force (fib2 (n-1))
type List a = Empty | Node a (Lazy (List a))
cons : a -> Lazy (List a) -> Lazy (List a)
cons first rest =
Lazy.lazy <| \() -> Node first rest
unsafeTail : Lazy (List a) -> Lazy (List a)
unsafeTail ll = case Lazy.force ll of
Empty -> Debug.crash "unsafeTail: empty lazy list"
Node _ t -> t
map2 : (a -> b -> c) -> Lazy (List a) -> Lazy (List b) -> Lazy (List c)
map2 f ll lr = Lazy.map2 (\l r -> case (l,r) of
(Node lh lt, Node rh rt) -> Node (f lh rh) (map2 f lt rt)
) ll lr
-- lazy list you can index into, better speed
fib3 = cons 0 (cons 1 (map2 (+) fib3 (unsafeTail fib3)))
一个包含所有斐波那fib3
契数的惰性列表也是如此。因为它在内部使用 fib3 本身,所以它将使用相同的(缓存的)惰性值,并且不需要进行太多计算。