2010-05-07 41 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))))) 

感谢大家!