Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 缓存一个";递归的;使用recur调用Clojure_Recursion_Clojure - Fatal编程技术网

Recursion 缓存一个";递归的;使用recur调用Clojure

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做“小阴谋家”和“经验丰富的阴谋家”练习,作为学习Scheme和Clojure的练习——尽可能用地道的Clojure来写。在第14章(经验丰富的策划家)中,他们定义了函数
最左边的
,该函数应该在列表中找到第一个“原子”(表示不是列表的实体,不是原子的克隆定义)。这是我在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毫秒” 真的 底线问题 因此,我终于(为冗长而抱歉)在两个问题上寻求帮助:

  • 是否有一种方法可以缓存或记忆原始else子句中的语句,而不必每次执行true/actual递归
  • 做这类事情最惯用的Clojure方法是什么?(可能是高阶函数或其他方法)
  • 不可以。嵌套列表是一棵树,您正在执行深度优先搜索。每次下拉列表时,您都需要跟踪以前的所有列表,以便在返回时继续检查它们。因此,堆栈消耗行为实际上不是由于存储返回值,而是由于遍历树

  • (第一个(展平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