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

发布于 2024-08-31 06:55:56 字数 1533 浏览 4 评论 0原文

我是函数式语言和 clojure 的新手,所以请耐心等待...

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

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

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

这是代码片段:

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

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

I'm new to functional languages and clojure, so please bear with me...

I'm trying to construct a list of functions, with either random parameters or constants. The function that constructs the list of functions is already working, though it doesn't return the function itself. I verified this using println.

(edit: Okay, it isn't working correctly yet after all)

(edit: Now it's working, but it cannot be "eval"-ed. it seems I need to recur at least two times, to ensure there are at least two children nodes. Is this possible?)

Here is the snippet:

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

Any help will be appreciated, thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

写给空气的情书 2024-09-07 06:55:56

这是我重写函数的尝试(请参阅下面的评论):

(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 语句,这让我想知道您是否有兴趣与一个生成函数并行构造一堆函数列表/代码> 调用。如果是这样,带有 recur 的尾递归可能不适合像这样的简单代码,因此您可以满足于常规的自调用(但请考虑达到递归限制的可能性;如果您确信您只会生成较小的函数,这不会成为问题,请继续进行自我调用)或研究连续传递样式并以该样式重写您的函数。

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

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

  4. 请注意,保留单独的 opcount Var 是没有意义的; Clojure 的持久列表(由 list 函数创建)会携带其计数,因此 (count some-list) 是一个恒定时间操作(并且非常快)。此外,将向量用于操作参数是惯用的(并且您可以切换到向量,而无需更改其余代码中的任何内容!)。例如 [\u \v \w \x \y \z]

  5. 在 Clojure 1.2 中,您将能够使用 (rand-nth coll) 代替 (nth coll (rand-int (count coll)))。< /p>

  6. 如果您想从表示操作、参数和常量的项目树生成实际的 Clojure 函数,您需要使用 eval。在大多数情况下,这是不鼓励的,但对于进化编程和类似的东西来说,这是唯一的选择。

  7. 就我个人而言,我会使用不同格式的 op/param/constant 映射:类似于 {:category foo, :content bar} ,其中 foo 是 < code>:op、:param:constbar 适合与任何给定的 foo< /code>.

Here's my shot at rewriting your function (see comments below):

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

And some examples of use from my 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})

A couple of things to keep in mind, in pretty random order:

  1. I used recur in the above to avoid consuming stack in the recursive self-calls. However, you have this dotimes statement which makes me wonder if you might be interested in constructing a bunch of function-lists in parallel with one generate-function call. If so, tail-recursion with recur might not be an option with simplistic code like this, so you could either settle for the regular self-calls (but do consider the possibility of hitting the recursion limit; if you're positive that you'll only generate smallish functions and this won't be a problem, go ahead with the self-calls) or investigate continuation-passing style and rewrite your function in that style.

  2. The (do (dec pc) ...) thing in your code does nothing to the value of pc in the next recursive call, or indeed to its current value. Local variables (or locals, as they are most often called in the community) in Clojure are immutable; this includes function parameters. If you want to pass along a decremented pc to some function, you'll have to do just that, like you did with maxdepth in an earlier branch of your code.

  3. I renamed your function to generate-function, because camel case in function names is quite unusual in Clojure land. Also, I renamed the parameter which you called function to function-list (so maybe I should have used a name like generate-function-list for the function... hm), because that's what it is for now.

  4. Note that there's no point to keeping a separate opcount Var around; Clojure's persistent lists (as created by the list function) carry their count around, so (count some-list) is a constant-time operation (and very fast). Also, it would be idiomatic to use vectors for operations and parameters (and you can switch to vectors without changing anything in the rest of the code!). E.g. [\u \v \w \x \y \z].

  5. In Clojure 1.2, you'll be able to use (rand-nth coll) for (nth coll (rand-int (count coll))).

  6. If you want to generate actual Clojure functions from trees of items representing ops, params and constants, you'll want to use eval. That's discouraged in most scenarios, but not for evolutionary programming and similar stuff where it's the only way to go.

  7. Personally, I'd use a different format of the op/param/constant maps: something like {:category foo, :content bar} where foo is :op, :param or :const and bar is something appropriate in connection to any given foo.

待"谢繁草 2024-09-07 06:55:56

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

In general it is a better idea in Clojure to use (recur ...) for your recursive functions. From the docs: "Note that recur is the only non-stack-consuming looping construct in Clojure." link

One other thing to note is that you might want to call the randomizer outside the recursive function, so you can define the stop-condition inside the function.

So like this:

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

It prints:

Let's define f with n = 4

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

where 4 is of course a random number between 0 (inclusive) and 10 (exclusive).

风透绣罗衣 2024-09-07 06:55:56

好吧,我发现我的做法是错误的。
树的递归定义无非是定义顶点,并尝试将所有内容与其联系起来。所以,我在不到 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)))))

谢谢大家!

So okay, I discovered I was going about this the wrong way.
A recursive definition of a tree is non other than defining vertices, and trying to tie everything with it. So, I came up with this, in less than 15 minutes. >_<

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

THANKS EVERYONE!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文