2017-02-21 9 views
0

は私が再発を使用するhttps://github.com/lspector/gp/blob/master/src/gp/evolvefn_zip.clj
からのコードのこの部分を書き換えるしようとしています:末尾再帰を正しく使うには?

(defn random-code [depth] 
    (if (or (zero? depth) 
      (zero? (rand-int 2))) 
    (random-terminal) 
    (let [f (random-function)] 
     (cons f (repeatedly (get function-table f) 
          #(random-code (dec depth))))))) 

問題があり、私はそれを行うにはどのように絶対にないアイデアを持っていません。それはちょうど私がそれを解決しようとする方法を示すことですが、コードの作業部分ではありません

(defn random-code [depth] 
    (loop [d depth t 0 c []] 
    (if (or (zero? depth) 
      (zero? (rand-int 2))) 
     (if (= t 0) 
     (conj c random-terminal) 
     (recur depth (dec t) (conj c (random-terminal)))) 
     (let [f (random-function)] 
     (if (= t 0) 
      (recur (dec depth) (function-table f) (conj c f)) 
      (recur depth (dec t) (conj c f))))))) 

、それだけで、ますます複雑になるだろう:私は考えることができる
唯一のことは、このようなものです。
clojureで通常の再帰を末尾の再帰に変換するより良い方法はありますか?ここで

+0

はい、ソリューションはコードのように畳み込まれている可能性が高くなります。あなたがツリーではなく、シーケンスを構築する理由。シーケンスを構築する場合は、通常、アキュムレータパラメータを追加します。このパラメータは、コード内のconjを置き換えます。 すべての関数が2つのchildreenを持つノードである場合、それは機能しません、両方の子にaccを送信することはできません。 なぜテール再帰が必要ですか? DEPTHがJavaスタックより大きいツリーを生成する場合にのみ必要です。 Javaは1000フレームの深さのスタックを持つことに問題はありません。テール再帰はおそらくあなたのバージョンよりも遅くなるでしょう。 – mattias

答えて

1

は、再帰的なアルゴリズムとloop-recurを比較した2例です:

(defn fact-recursion [n] 
    (if (zero? n) 
    1 
    (* n (fact-recursion (dec n))))) 

(defn fact-recur [n] 
    (loop [count n 
     result 1] 
    (if (pos? count) 
     (recur (dec count) (* result count)) 
     result))) 

(fact-recursion 5) => 120 
(fact-recur 5) => 120 

(defn rangy-recursive [n] 
    (if (pos? n) 
    (cons n (rangy-recursive (dec n))) 
    [n])) 

(defn rangy-recur [n] 
    (loop [result [] 
     count n] 
    (if (pos? count) 
     (recur (conj result count) (dec count)) 
     result))) 

(rangy-recursive 5) => (5 4 3 2 1 0) 
(rangy-recur 5) => [5 4 3 2 1] 

基本的な違いはloop-recurのためにあなたがアルゴリズムの出力を蓄積するために(ここではresultという名前の)第二ループ「変数を」必要があるということです。単純な再帰の場合、コールスタックは中間結果を累積します。

関連する問題