我可以在clojure中功能组成的实现中使用`recur'吗?
考虑到comp
在clojure中的这种简单的递归实现:
(defn my-comp
([f]
(fn [& args]
(apply f args)))
([f & funcs]
(fn [& args]
(f (apply (apply my-comp funcs) args)))))
我被告知正确的方法是使用recur
,但我不确定recor> recur>
有效。特别是:有没有办法将上述代码哄骗到recur
能够?
Consider this simple-minded recursive implementation of comp
in Clojure:
(defn my-comp
([f]
(fn [& args]
(apply f args)))
([f & funcs]
(fn [& args]
(f (apply (apply my-comp funcs) args)))))
The right way to do this, I am told, is using recur
, but I am unsure how recur
works. In particular: is there a way to coax the code above into being recur
able?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
评估1
首先让我们想象一下问题。
my-comp
在问题中编写时,将创建一个深层的函数调用,每个函数呼叫都在堆栈上等待解决,直到最深的呼叫返回  --comp
而不是创建较长的函数序列,而是重构的
my-comp
以返回a single 函数,当调用时,该函数运行a> loop
在提供的输入功能上 -evaluation 1
First let's visualize the problem.
my-comp
as it is written in the question will create a deep stack of function calls, each waiting on the stack to resolve, blocked until the the deepest call returns -tail-recursive my-comp
Rather than creating a long sequence of functions, this
my-comp
is refactored to return a single function, which when called, runs aloop
over the supplied input functions -evaluation 2
With
my-comp
rewritten to useloop
andrecur
, we can see linear iterative evaluation of the composition -multiple input args
Did you notice ten (10)
apply
calls at the beginning of this post? This is all in service to support multiple arguments for the first function in themy-comp
sequence. It is a mistake to tangle this complexity withmy-comp
itself. The caller has control to do this if it is the desired behavior.Without any additional changes to the refactored
my-comp
-Which evaluates as -
right-to-left order
Above
(my-comp a b c)
will applya
first, thenb
, and finallyc
. If you want to reverse that order, a naive solution would be to callreverse
at theloop
call site -Each time the returned function is called,
(reverse fs)
will be recomputed. To avoid this, use alet
binding to compute the reversal just once -做到这一点的一种方法是重新排列此代码,以将一些中间功能传递给重复出现的定义。
该模型将是这样的:
Overload(
(my-comp [f])
- comp
最后一个my-comp将使用第一个my 表格仍然可以接受variadic参数作为序列。
作为可能的
应用
目标,recur
您的:recur
使您免于堆栈溢出的函数创建,它仍然会在应用程序上溢出(有人,如果我错了,请纠正我):因此,使用
降低可能会更好
或其他东西:a way to do this, is to rearrange this code to pass some intermediate function back up to the definition with recur.
the model would be something like this:
the last my-comp would use the first my-comp overload (which is
(my-comp [f])
here's how it could look like:
notice that despite of not being the possible
apply
target, therecur
form can still accept variadic params being passed as a sequence.notice, though, that in practice this implementation isn't really better than yours: while
recur
saves you from stack overflow on function creation, it would still overflow on application (somebody, correct me if i'm wrong):so it would probably be better to use
reduce
or something else:上面已经有一个很好的答案,但是我认为使用
recur
的原始建议可能一直在考虑结果的手动积累。如果您还没有看过它,降低
只是loop/recur
的非常具体的用法:通常,可以有任何数量的循环变量(不仅仅是2个循环变量喜欢减少)。使用
loop/recur
为您提供了使用累积状态而不是使用和atom
和doseq
或其他东西的“功能性”循环方式。顾名思义,从外部,效果与任何堆栈尺寸限制(即尾尾优化)的正常递归非常相似。ps 如本示例所示,我喜欢使用
let
表格非常明确地命名为下一次迭代生成的值。pps 虽然编译器允许您键入以下混乱:
重新使用变量名称可能会有些混乱和/或草率(尤其是针对新手或您的特定的人程序)。
另外,
如果我没有指出功能定义本身可以是
recur
target(即您不需要使用loop
),我也会被解雇。考虑此版本的阶乘:There is already a good answer above, but I think the original suggestion to use
recur
may have been thinking of a more manual accumulation of the result. In case you haven't seen it,reduce
is just a very specific usage ofloop/recur
:In general, there can be any number of loop variables (not just 2 like for reduce). Using
loop/recur
gives you a more "functional" way of looping with accumulated state instead of using andatom
and adoseq
or something. As the name suggests, from the outside the effect is quite similar to a normal recursion w/o any stack size limits (i.e. tail-call optimization).P.S. As this example shows, I like to use a
let
form to very explicitly name the values being generated for the next iteration.P.P.S. While the compiler will allow you to type the following w/o confusion:
it can be a bit confusing and/or sloppy to re-use variable names (esp. for people new to Clojure or your particular program).
Also
I would be remiss if I didn't point out that the function definition itself can be a
recur
target (i.e. you don't need to useloop
). Consider this version of the factorial: