如何在Clojure中返回递归函数的输出

时间:2010-05-07 04:55:33

标签: functional-programming recursion lisp 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))))))

任何帮助将不胜感激,谢谢!

3 个答案:

答案 0 :(得分: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 user$fn__4557@6cbb2d>} {:const 77} {:param \w} {:op #<user$fn__4559 user$fn__4559@8e68bd>} {:const 3} {:param \v} {:const 1} {:const 8} {:op #<user$fn__4559 user$fn__4559@8e68bd>} {:op #<user$fn__4555 user$fn__4555@6f0962>})
user> (generate-function)
({:const 27} {:param \y} {:param \v} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:const 61})

要记住几件事,按照相当随机的顺序:

  1. 我在上面使用了recur来避免在递归自调用中使用堆栈。但是,你有这个dotimes语句让我想知道你是否有兴趣与一个function-list调用同时构建一堆generate-function s。如果是这样的话,使用recur的尾递归可能不是这样的简单代码的选项,所以你可以选择常规的自调用(但是考虑到达到递归限制的可能性;如果你是积极的,你只会产生小的功能,这不会是一个问题,继续自我调用)或调查继续传递的风格,并以这种风格重写你的功能。

  2. 代码中的(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 / constant地图:{:category foo, :content bar} foo:op:param:constbar与任何给定的foo相关。

答案 1 :(得分:2)

一般来说,在Clojure中使用(recur ...)作为递归函数是一个更好的主意。从文档:“请注意,recur是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(不含)之间的随机数。

答案 2 :(得分:1)

好吧,我发现我的方式错了。 树的递归定义不仅仅是定义顶点,而是尝试将所有内容与之绑定在一起。所以,我在不到15分钟的时间内想到了这一点。 &GT; _&LT;

(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)))))

感谢每个人!