如何在 Clojure 中返回递归函数的输出
我是函数式语言和 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是我重写函数的尝试(请参阅下面的评论):
以及我的 REPL 中的一些使用示例...
需要记住的几件事,顺序相当随机:
我使用了
recur
避免在递归自调用中消耗堆栈。但是,您有这个 dotimes 语句,这让我想知道您是否有兴趣与一个生成函数并行构造一堆函数列表/代码> 调用。如果是这样,带有recur
的尾递归可能不适合像这样的简单代码,因此您可以满足于常规的自调用(但请考虑达到递归限制的可能性;如果您确信您只会生成较小的函数,这不会成为问题,请继续进行自我调用)或研究连续传递样式并以该样式重写您的函数。代码中的
(do (dec pc) ...)
对下一个递归调用中的pc
的值没有任何作用,或者实际上对其当前的值没有任何作用价值。 Clojure 中的局部变量(或本地变量,因为它们在社区中最常被称为)是不可变的;这包括函数参数。如果您想将递减的pc
传递给某个函数,则必须这样做,就像您在代码的早期分支中使用maxdepth
所做的那样。< /p>我将你的函数重命名为
generate-function
,因为函数名称中的驼峰式大小写在 Clojure 领域很不寻常。另外,我将您称为function
的参数重命名为function-list
(所以也许我应该使用像generate-function-list
这样的名称对于函数...嗯),因为现在就是这样。请注意,保留单独的
opcount
Var 是没有意义的; Clojure 的持久列表(由list
函数创建)会携带其计数,因此(count some-list)
是一个恒定时间操作(并且非常快)。此外,将向量用于操作
和参数
是惯用的(并且您可以切换到向量,而无需更改其余代码中的任何内容!)。例如[\u \v \w \x \y \z]
。在 Clojure 1.2 中,您将能够使用
(rand-nth coll)
代替(nth coll (rand-int (count coll)))
。< /p>如果您想从表示操作、参数和常量的项目树生成实际的 Clojure 函数,您需要使用
eval
。在大多数情况下,这是不鼓励的,但对于进化编程和类似的东西来说,这是唯一的选择。就我个人而言,我会使用不同格式的 op/param/constant 映射:类似于
{:category foo, :content bar}
,其中foo
是 < code>:op、:param
或:const
和bar
适合与任何给定的foo< /code>.
Here's my shot at rewriting your function (see comments below):
And some examples of use from my REPL...
A couple of things to keep in mind, in pretty random order:
I used
recur
in the above to avoid consuming stack in the recursive self-calls. However, you have thisdotimes
statement which makes me wonder if you might be interested in constructing a bunch offunction-list
s in parallel with onegenerate-function
call. If so, tail-recursion withrecur
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.The
(do (dec pc) ...)
thing in your code does nothing to the value ofpc
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 decrementedpc
to some function, you'll have to do just that, like you did withmaxdepth
in an earlier branch of your code.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 calledfunction
tofunction-list
(so maybe I should have used a name likegenerate-function-list
for the function... hm), because that's what it is for now.Note that there's no point to keeping a separate
opcount
Var around; Clojure's persistent lists (as created by thelist
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 foroperations
andparameters
(and you can switch to vectors without changing anything in the rest of the code!). E.g.[\u \v \w \x \y \z]
.In Clojure 1.2, you'll be able to use
(rand-nth coll)
for(nth coll (rand-int (count coll)))
.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.Personally, I'd use a different format of the op/param/constant maps: something like
{:category foo, :content bar}
wherefoo
is:op
,:param
or:const
andbar
is something appropriate in connection to any givenfoo
.一般来说,在 Clojure 中使用 (recur ...) 来实现递归函数是一个更好的主意。来自文档:“请注意,recur 是 Clojure 中唯一不消耗堆栈的循环结构。” link
另一件需要注意的事情是,您可能想在递归函数之外调用随机生成器,因此您可以在函数内定义停止条件。
像这样:
它打印:
其中 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:
It prints:
where 4 is of course a random number between 0 (inclusive) and 10 (exclusive).
好吧,我发现我的做法是错误的。
树的递归定义无非是定义顶点,并尝试将所有内容与其联系起来。所以,我在不到 15 分钟的时间内想出了这个办法。 >_<
谢谢大家!
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. >_<
THANKS EVERYONE!