【问题标题】:How to return the output of a recursive function in Clojure如何在 Clojure 中返回递归函数的输出
【发布时间】:2011-02-16 16:42:48
【问题描述】:

我是函数式语言和 clojure 的新手,所以请多多包涵……

我正在尝试使用随机参数或常量构建函数列表。构造函数列表的函数已经在工作,尽管它不返回函数本身。我使用 println 验证了这一点。

(编辑:好的,毕竟它还不能正常工作)

(编辑:现在它正在工作,但它不能是“eval”-ed。看来我需要至少重复两次,以确保至少有两个子节点。这可能吗?)

这里是sn-p:

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

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

【问题讨论】:

    标签: functional-programming recursion lisp clojure


    【解决方案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)))))
    

    谢谢大家!

    【讨论】:

      【解决方案2】:

      这是我重写函数的尝试(见下面的 cmets):

      (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 声明,这让我想知道你是否有兴趣在一个generate-function 调用的同时构建一堆function-lists。如果是这样,使用recur 的尾递归可能不是像这样简单代码的选项,因此您可以满足于常规的自调用(但请考虑达到递归限制的可能性;如果您肯定您只会生成较小的函数,这不会成为问题,继续进行自我调用)或研究延续传递样式并以该样式重写您的函数。

      2. 代码中的(do (dec pc) ...) 对下一次递归调用中的pc 的值或实际上对其当前值没有任何作用。 Clojure 中的局部变量(或本地变量,因为它们在社区中最常被称为)是不可变的;这包括函数参数。如果您想将递减的 pc 传递给某个函数,则必须这样做,就像您在代码的早期分支中对 maxdepth 所做的那样。

      3. 我将您的函数重命名为 generate-function,因为函数名称中的驼峰式大小写在 Clojure 领域中非常不常见。另外,我将您称为function 的参数重命名为function-list(所以也许我应该为函数使用类似generate-function-list 的名称......嗯),因为现在就是这样。

      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. 如果您想从表示操作、参数和常量的项目树中生成实际的 Clojure 函数,您需要使用eval。在大多数情况下不鼓励这样做,但对于进化编程和类似的东西,这是唯一可行的方法。

      7. 1234563与任何给定的foo 相关的适当内容。

      【讨论】:

        【解决方案3】:

        一般来说,在 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(不含)之间的随机数。

        【讨论】:

        • 我很感谢你给我的建议,但我想我应该让你们知道我的意图。我的目标是创建一个函数,用作遗传编程问题中的种群。 @Michal - 我不明白我怎么能(评估)这个...... @Michiel - 我明白了。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-29
        • 2017-10-29
        • 1970-01-01
        • 1970-01-01
        • 2017-11-02
        相关资源
        最近更新 更多