(def fibVal {1 1 2 1})
(defn fibonacci [x]
(if (false? (get fibVal x false))
(do
(println (str "Evaluating " x))
(def fibVal (assoc fibVal x (+ (fibonacci(- x 1)) (fibonacci(- x 2)))))
(println (str x " Evaluated to " (fibVal x)))
(fibVal x)
)
(get fibVal x)
)
)
(斐波那契5)的输出
评估5
评估4
评估3
3评估为2
4评估为3
评估3
3评估为2
5评估为5
5
3被评估两次,而在memoized版本中,它只应评估一次。
答案 0 :(得分:4)
在除顶级表单之外的任何内容中使用def
都不是线程安全的,并且不能保证在您使用它时起作用。为了存储像这样改变的状态,你很可能想要使用一个可变状态选项,如atoms,refs或agents。
在这种情况下,原子将是一个不错的选择。
答案 1 :(得分:2)
您的程序存在以下形式:
(def fibVal (assoc fibVal
x (+ (fibonacci (- x 1))
(fibonacci (- x 2)))))
在递归调用写入新版本之前,将在第一行中使用的fibVal
计算为其当前值。当fibVal
表达式最终计算时,它们对def
var所做的任何操作都将被遗忘,因为它们在被调用之前变为fibVal
,其返回值的总和与x
相关联{1}}。
def
旨在用于顶级声明,而不是用于在递归过程中改变全局变量。
此外,您的递归实现不是迭代的,因此它会将堆栈吹得足够高。
以下是带有memoization的有状态迭代实现的示例:
(def fib-cache (atom [0 1]))
(defn- calc-nth-fib
[fibs n]
(reduce (fn [fibs n]
(assoc fibs n
(apply + (take 2 (rseq fibs)))))
fibs
(range (count fibs) (inc n))))
(defn fibonacci [x]
(or (get @fib-cache x)
(-> fib-cache
(swap! calc-nth-fib x)
(nth x))))
请注意,此示例并不代表在Clojure中找到第n个Fibonacci的惯用方法,因为这需要在设计了延迟序列的单个线程上生成直到第n个数字的整个序列。它们隐式提供缓存,并针对所需的用例进行了优化。
对于惯用的Fibonacci实现,请参考众多lazy Fibonacci implementations中的一个,如有必要learn about lazy sequences.