2010-05-07 46 views
3

我是新來的函數式語言和Clojure的,所以請多多包涵......如何返回遞歸函數的輸出Clojure中

我試圖構建的功能列表,無論是隨機參數或常量。構造函數列表的函數已經工作,儘管它不會返回函數本身。我使用println驗證了這一點。

(編輯:好吧,這是不正確的工作尚未畢竟)

(編輯:現在,它的工作,但它不能是「EVAL」 -ed看來我需要再次出現至少兩個。次,以保證至少有兩個孩子節點這是可能的)

下面是摘錄:?

(def operations (list #(- %1 %2) #(+ %1 %2) #(* %1 %2) #(/ %1 %2))) 
(def parameters (list \u \v \w \x \y \z)) 
(def parameterlistcount 6) 
(def paramcount 2) 
(def opcount 4) 

(defn generate-function 


([] (generate-function 2 4 0.5 0.6() parameters)) 
    ([pc maxdepth fp pp function-list params] 
    (if (and (pos? maxdepth) (< (rand) fp)) 
     (let [function-list 
      (conj function-list 
        (nth operations 
         (rand-int (count operations))))] 
     (recur pc (dec maxdepth) fp pp function-list params)) 
     (if (and (< (rand) pp) (pos? pc)) 
     (let [ params (pop parameters) 
     function-list 
       (conj function-list 
         (nth params 
         (rand-int (count params))))] 
     (if (contains? (set operations) (last function-list)) 
      (recur (dec pc) maxdepth fp pp function-list params) 
      nil)) 
     (let [function-list 
       (conj function-list 
         (rand-int 100))] 
      (if (or (pos? maxdepth) (pos? pc)) 
      (if (contains? (set operations) (last function-list)) 
     (recur pc maxdepth fp pp function-list params) 
     nil) 
      function-list)))))) 

任何幫助將不勝感激,謝謝!

回答

2

一般來說,在Clojure中使用遞歸函數(recur ...)是一個更好的主意。從文檔:「請注意,repeat是Clojure中唯一非堆棧消耗循環構造。」 link

還有一點需要注意的是,你可能想在遞歸函數外調用隨機函數,所以你可以在函數內定義停止條件。

所以這樣的:

(let [n (rand-int 10)] 
    (println "Let's define f with n =" n) 
    (defn f [x] 
    (if (> x n) 
     "result" 
     (do (println x) 
      (recur (inc x)))))) 

它打印:

Let's define f with n = 4 

user> (f 0) 
0 
1 
2 
3 
4 
"result" 

其中4是當然的介於0(含)和10(不包括)的隨機數。

+0

即時感謝你給我的建議,但我想我應該讓你們知道我的意圖。 我的目標是創建一個函數,在基因編程問題中用作羣體。 @Michal - 我不明白我怎麼能(評估)這個... @Michiel - 我明白了。 – Silanglaya 2010-05-11 02:01:44

3

這是我在拍你重寫功能(參見下面的註釋):

(defn generate-function 
    ([] (generate-function 2 4 0.5 0.6())) 
    ([pc maxdepth fp pp function-list] 
    (if (and (pos? maxdepth) (< (rand) fp)) 
     (let [function-list 
      (conj function-list 
        {:op 
        (nth operations 
         (rand-int (count operations)))})] 
     (recur pc (dec maxdepth) fp pp function-list)) 
     (if (and (< (rand) pp) (pos? pc)) 
     (let [function-list 
       (conj function-list 
        {:param 
         (nth parameters 
          (rand-int (count parameters)))})] 
      (recur (dec pc) maxdepth fp pp function-list)) 
     (let [function-list 
       (conj function-list 
        {:const 
         (rand-int 100)})] 
      (if (or (pos? maxdepth) (pos? pc)) 
      (recur pc maxdepth fp pp function-list) 
      function-list)))))) 

從我REPL使用的一些例子...

user> (generate-function) 
({:const 63} {:op #<user$fn__4557 [email protected]>} {:const 77} {:param \w} {:op #<user$fn__4559 [email protected]>} {:const 3} {:param \v} {:const 1} {:const 8} {:op #<user$fn__4559 [email protected]>} {:op #<user$fn__4555 [email protected]>}) 
user> (generate-function) 
({:const 27} {:param \y} {:param \v} {:op #<user$fn__4561 [email protected]>} {:op #<user$fn__4561 [email protected]>} {:op #<user$fn__4561 [email protected]>} {:op #<user$fn__4561 [email protected]>} {:const 61}) 

幾件事情要記住,以相當隨機的順序:

  1. 我在上面用recur來避免消耗堆棧在遞歸的自我調用。然而,你有這個dotimes聲明,這讓我想知道你是否有興趣與一個generate-function呼叫同時構建一堆function-list。如果是這樣,用recur進行尾遞歸可能不是一個簡單代碼的選項,所以你可以選擇定期進行自我調用(但是要考慮是否有可能達到遞歸限制;如果你確信你是「只會產生一些小功能,這不會成爲問題,請繼續進行自我調用),或者調查延續傳遞風格並重寫該風格的函數。

  2. (do (dec pc) ...)(do (dec pc) ...)代碼中的東西對下一次遞歸調用中的值pc沒有影響,實際上與當前值無關。 Clojure中的局部變量(或當地人,因爲他們通常在社區中被稱爲)是不可變的;這包括功能參數。如果您想將遞減的pc傳遞給某個函數,那麼您必須這樣做,就像您在代碼的較早分支中使用maxdepth一樣。

  3. 我把你的函數改名爲generate-function,因爲函數名中的駱駝情況在Clojure地很不尋常。另外,我將名爲function的參數重命名爲function-list(所以也許我應該使用像generate-function-list這樣的名稱作爲... hm),因爲這就是現在的情況。

  4. 請注意,沒有必要單獨保留opcount Var; Clojure的持久性列表(由list函數創建)進行計數,因此(count some-list)是一個恆定時間操作(並且速度非常快)。另外,使用operationsparameters的載體也是習慣用法(並且您可以切換到矢量而不改變其餘代碼中的任何內容!)。例如。 [\u \v \w \x \y \z]

  5. 在Clojure 1.2中,您可以使用(rand-nth coll)(nth coll (rand-int (count coll)))

  6. 如果要從代表ops,params和常量的項目樹中生成實際的Clojure函數,則需要使用eval。在大多數情況下,這是不鼓勵的,但不適用於進化編程和類似的東西,這是唯一的出路。

  7. 個人而言,我使用op/PARAM /恆定圖的不同格式:像{:category foo, :content bar}其中foo:op:param:constbar是在連接到任何給定的foo合適的東西。

1

所以好吧,我發現我正在做這個錯誤的方式。 樹的遞歸定義不是定義頂點,而是試圖將所有東西都與它綁定。所以,我在不到15分鐘內就提出了這個問題。 > _ <

(defn generate-branch 
"Generate branches for tree" 
    ([] (generate-branch 0.6() (list \x \y \z))) 
    ([pp branch params] 
     (loop [branch 
     (conj branch (nth operations (rand-int (count operations))))] 
    (if (= (count branch) 3) 
     branch 
     (if (and (< (rand) pp)) 
     (recur (conj branch (nth params (rand-int (count params))))) 
     (recur (conj branch (rand-int 100)))))))) 

(defn build-vertex 
"Generates a vertex from branches" 
    [] 
    (let [vertex (list (nth operations (rand-int (count operations))))] 
    (conj vertex (take 5 (repeatedly generate-branch))))) 

感謝大家!