Recursion 缓存一个";递归的;使用recur调用Clojure
我正在用Clojure做“小阴谋家”和“经验丰富的阴谋家”练习,作为学习Scheme和Clojure的练习——尽可能用地道的Clojure来写。在第14章(经验丰富的策划家)中,他们定义了函数Recursion 缓存一个";递归的;使用recur调用Clojure,recursion,clojure,Recursion,Clojure,我正在用Clojure做“小阴谋家”和“经验丰富的阴谋家”练习,作为学习Scheme和Clojure的练习——尽可能用地道的Clojure来写。在第14章(经验丰富的策划家)中,他们定义了函数最左边的,该函数应该在列表中找到第一个“原子”(表示不是列表的实体,不是原子的克隆定义)。这是我在Clojure中实现的真正递归版本: (defn atom? [x] (not (coll? x))) (defn leftmost [l] (cond (empty? l) [] (at
最左边的
,该函数应该在列表中找到第一个“原子”(表示不是列表的实体,不是原子的克隆定义)。这是我在Clojure中实现的真正递归版本:
(defn atom? [x]
(not (coll? x)))
(defn leftmost [l]
(cond
(empty? l) []
(atom? (first l)) (first l)
:else (let [r (leftmost (first l))]
(if (atom? r)
r
(leftmost (rest l))))))
为了明确它的作用,以下是它的测试:
(deftest test-leftmost
(is (= :a (leftmost [:a :b [:c :d]])))
(is (= :a (leftmost [[:a :b] [:c :d]])))
(is (= :a (leftmost [[] [] [[:a]] :b [:c :d]])))
(is (= [] (leftmost-recur [[] [['()]]])))
(is (= [] (leftmost-recur []))))
练习的关键部分是通过在:else
子句中使用let语句来“缓存”最左侧(第一个l)的调用
我想使用recur和get-tail调用优化在Clojure中编写这篇文章。到目前为止,我能做的最好的事情是:
(defn leftmost-recur [l]
(cond
(empty? l) []
(atom? (first l)) (first l)
:else (let [r (leftmost-recur (first l))]
(if (atom? r)
r
(recur (rest l))))))
在:else子句中,我仍然得到了一个真正的递归,而不是recur,因为recur当然必须出现在尾部调用位置。此函数通过了测试,但存在真正的递归问题,包括堆栈溢出
有没有一种方法可以“缓存”(让[r(最左边的重复出现(第一个l))]
调用而不执行真正的递归
尝试使用memoize
我试着考虑使用记忆
,但我不确定如何记忆一个自递归函数。这是我的尝试,但我认为它没有达到我所希望的效果:
(defn leftmost-recur-memoize [l]
(Thread/sleep 100) ; added to check whether memoize is working
(let [memo-leftmost (memoize leftmost-recur-memoize)]
(cond
(empty? l) []
(atom? (first l)) (first l)
:else (let [r (memo-leftmost (first l))]
(if (atom? r)
r
(memo-leftmost (rest l))
)))))
…根据测试编号:
(println (time (= :a (leftmost-recur-memoize [:a :b [:c :d]]))))
(println (time (= :a (leftmost-recur-memoize [[] [] [[:a]] :b [:c :d]]))))
(println (time (= [] (leftmost-recur-memoize [[] [['()]]]))))
;; repeat same
(println (time (= :a (leftmost-recur-memoize [:a :b [:c :d]]))))
(println (time (= :a (leftmost-recur-memoize [[] [] [[:a]] :b [:c :d]]))))
(println (time (= [] (leftmost-recur-memoize [[] [['()]]]))))
“运行时间:100.27427毫秒”
真的
“运行时间:701.740783毫秒”
真的
“运行时间:801.796439毫秒”
真的
“运行时间:100.148838毫秒”
真的
“运行时间:701.430802毫秒”
真的
“运行时间:801.767962毫秒”
真的
底线问题
因此,我终于(为冗长而抱歉)在两个问题上寻求帮助:
(第一个(展平coll))
谢谢Alex,非常有帮助。我快速查看了Clojure的Flatte源代码,它本身似乎是递归的(不是伪递归的),因此对于这种树行走,需要真正的递归。 "Elapsed time: 100.27427 msecs" true "Elapsed time: 701.740783 msecs" true "Elapsed time: 801.796439 msecs" true "Elapsed time: 100.148838 msecs" true "Elapsed time: 701.430802 msecs" true "Elapsed time: 801.767962 msecs" true