循环的尾部位置到底是什么?
Clojure 中 recur
的“尾部位置”的准确定义是什么?我认为这将是循环 S 表达式中的最后一项,但在下面的示例中,在我看来,以 (if ...)
开头的 S 表达式位于尾部位置即(循环[绑定语句][if 语句])。
(= __
(loop [x 5
result []]
(if (> x 0)
(recur (dec x) (conj result (+ 2 x)))
result)))
(代码取自http://www.4clojure.com/problem/68)
密切相关问题: 如何调用 recur在 Clojure 的 if 条件中?
What's the precise definition of "tail position" for recur
in Clojure? I would think that it would be the last item in a loop S-expression, but in the example below it seems to me that the S-Expression which starts with (if ...)
is in tail position i.e. (loop [binding statements] [if statement]).
(= __
(loop [x 5
result []]
(if (> x 0)
(recur (dec x) (conj result (+ 2 x)))
result)))
(Code taken from http://www.4clojure.com/problem/68)
Closely related question: How can I call recur in an if conditional in Clojure?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
尾部位置是表达式返回值的位置。评估尾部位置的表单后,不再评估任何表单。
请考虑 Clojure 的乐趣
它采用单个参数并将其命名为 x。如果 x 已经是正数,则 x 是
返回;否则返回 x 的相反数。
if 形式位于函数的尾部位置,因为无论它返回什么,整个
函数将返回。 “then”子句中的 x 也位于函数的尾部位置。
但是“else”子句中的 x 不在函数的尾部位置,因为 x 的值
传递给 - 函数,而不是直接返回。 else 子句作为一个整体 (- x) 位于
尾部位置。
同样,在表达式中,
b
和c
都位于尾部位置,因为它们中的任何一个都可以从 if 语句返回。现在,在您的示例中,
(if ...)
表单位于(loop ...)
表单和(recur .. .)
表单和result
表单位于(if ...)
表单的尾部位置。另一方面,在您链接的问题中,
recur
not 位于尾部位置,因为(+ 1 ...)
将被评估在(recur tail)
之后。因此 Clojure 编译器会给出错误。尾部位置很重要,因为您可以从尾部位置使用
recur
形式。函数式编程语言通常使用递归来实现过程式编程语言通过循环完成的任务。但递归是有问题的,因为它消耗堆栈空间,并且深度递归可能导致堆栈溢出问题(除了速度慢之外)。这个问题通常通过尾部调用优化(TCO)来解决,当递归调用发生在函数/表单的尾部位置时,它会消除调用者。因为 Clojure 托管在 JVM 上,而 JVM 不支持尾调用优化,所以它需要一个技巧来进行递归。
recur
形式就是这个技巧,它允许 Clojure 编译器执行类似于尾部调用优化的操作。此外,它还验证recur
确实位于尾部位置。好处是您可以确保优化确实发生。The tail position is a position which an expression would return a value from. There are no more forms evaluated after the form in the tail position is evaluated.
Consider this example from The Joy of Clojure
It takes a single parameter and names it x. If x is already a positive number, then x is
returned; otherwise the opposite of x is returned.
The if form is in the function’s tail position because whatever it returns, the whole
function will return. The x in the “then” clause is also in a tail position of the function.
But the x in the “else” clause is not in the function’s tail position because the value of x
is passed to the - function, not returned directly. The else clause as a whole (- x) is in
a tail position.
Similarly in the expression
both
b
andc
are in tail positions, because either of them could be returned from the if statement.Now in your example
the
(if ...)
form is in the tail position of the(loop ...)
form and both the(recur ...)
form and theresult
form are in the tail position of the(if ...)
form.On the other hand in the question that you linked
the
recur
is not in tail position because the(+ 1 ...)
will be evaluated after the(recur tail)
. Therefore the Clojure compiler gives an error.Tail position is important because you can use the
recur
form from tail position. Functional programming languages usually use recursion for what procedural programming languages accomplish by loops. But recursion is problematic, because it consumes stack space and deep recursion can lead to stackoverflow problems (in addition to being slow). This problem is usually solved by tail call optimization (TCO), which does away with the caller when the recursive call happens in the tail position of a function / form.Because Clojure is hosted on the JVM and the JVM does not support tail call optimization, it needs a trick to do recursion. The
recur
form is that trick, it allows the Clojure compiler to do something similar to tail call optimization. In addition, it verifies thatrecur
is indeed in a tail position. The benefit is that you can make sure that the optimization actually does happen.只是为了补充上面 Paul 的精彩答案,The Joy of Clojure (ed1) 提供了一个表格(表 7.1),它准确地显示了各种形式/表达式中尾部位置的位置,我在下面复制了该表格。查找“tail”一词在每种形式/表达中出现的位置:
Just to supplement the excellent answer from Paul above, The Joy of Clojure (ed1) provides a table (Table 7.1) that shows exactly where tail position is in various forms/expressions, which I've reproduced below. Look for where the word "tail" occurs in each form/expression: