如何在Clojure中将非尾递归与同步memoization和有界堆栈消耗相结合(因此没有堆栈溢出的风险)?通过同步memoization,我的意思是必须在线程之间同时有效地共享备忘录/缓存。
我的具体情况如下:
; g() is non recursive
; i is an integer
; h is a hash with int keywords and vector of ints values
; w is a hash with int keywords and int values
(defn g [i h w]
(filter
#(-> (w %)
(= i))
(h i)))
; f is recursive, recurses non-trivially (non-tail, multiple times)
; TODO: be memoizable (ideally in a synchronized way, for parallelism)
; TODO: pose no risk stack overflow
(defn f [i h w]
(if (nil? (h i))
0
(let [part_sum
(map ; will change this map to pmap or pvmap
#(f % h w)
(g i h))]
(-> (reduce + part_sum)
(/ 2)
(+ 1)))))
; trivial, shown for completeness
(defn ff [i h w]
(-> (f i h w)
(- 1)
(* 2)
(max 0)))
答案 0 :(得分:2)
幸运的是,这些问题可以独立解决:
对于问题1,您需要首先确定应在何时填充缓存。是否应该在开始计算功能时填充。这意味着应该绝对保证每个函数只运行一次,即使在第一个函数运行时进行了第二次调用。或者,如果要允许对函数的两次调用同时发生,并且只将其中一个调用存储到缓存中。稍有不同的是,您只需将最后返回的结果存储到缓存中。
如果您只是致电
,最后一种方法就是您默认获得的方法(def memoized-function (memoize function-name))
几乎在所有情况下,它都是足够的。如果您需要其他选项,那么使您想要记忆的功能返回future
而不是结果,并且只有deref
在您使用它们之前从缓存中获得的值。
对于选项二,内置的trampoline
函数允许您具有常量堆栈非尾递归函数。您更改函数以在基本情况下(递归完成时)返回不是函数的值(只是正常结果),并在需要进一步递归时返回函数。然后trampoline
功能"反弹"反复进入函数,直到一个值从另一侧掉出来。它看起来像这样:
user> (defn foo-helper [x]
(let [result
(if (pos? x)
#(foo-helper (dec x))
x)]
(println "foo" x)
result))
#'user/foo-helper
user> (trampoline foo-helper 4)
foo 4
foo 3
foo 2
foo 1
foo 0
0
因此,您可以将Clojure的正常缓存与正常的trampline函数调用结合起来,而不必担心"线程安全"