假牙复发的“尾部位置”的确切定义是什么?我认为这将是循环S表达式中的最后一项,但在下面的示例中,我看来,以(if ...)开头的S表达式位于结尾位置,即([LOOP KEYWORD] [绑定(bind)声明] [如果声明]。
(= __
(loop [x 5
result []]
(if (> x 0)
(recur (dec x) (conj result (+ 2 x)))
result)))
来自http://www.4clojure.com/problem/68的代码
密切相关的问题:How can I call recur in an if conditional in Clojure?
最佳答案
尾部位置是表达式从中返回值的位置。在评估位于尾部位置的表格之后,不再评估任何表格。
考虑来自The Joy of Clojure的示例
(defn absolute-value [x]
(if (pos? x)
x ; "then" clause
(- x))) ; "else" clause
它采用一个参数并将其命名为x。如果x已经是一个正数,则x为
回;否则,返回x的反面。
if形式在函数的尾部位置,因为无论返回什么,整个
函数将返回。 “then”子句中的x也在函数的尾部位置。
但是“else”子句中的x不在函数的尾部位置,因为x的值
传递给-函数,不直接返回。 else子句作为一个整体(-x)位于
尾巴位置。
表达式中类似
(if a
b
c)
b
和c
都位于尾部,因为它们都可以从if语句中返回。现在在您的示例中
(loop [x 5
result []]
(if (> x 0)
(recur (dec x) (conj result (+ 2 x)))
result)))
(if ...)
形式位于(loop ...)
形式的尾部,并且(recur ...)
形式和result
形式均处于(if ...)
形式的尾部。另一方面,您链接的问题
(fn [coll] (let [tail (rest coll)]
(if (empty tail)
1
(+ 1 (recur tail)))))
尾部位置的
recur
是而不是,因为(+ 1 ...)
将在(recur tail)
之后评估。因此,Clojure编译器给出了一个错误。尾巴位置很重要,因为您可以从尾巴位置使用
recur
形式。函数式编程语言通常使用递归来实现过程式编程语言通过循环完成的工作。但是递归是有问题的,因为它会占用堆栈空间,而深度递归会导致堆栈溢出问题(除了速度较慢)。通常通过尾部调用优化(TCO)解决此问题,当递归调用发生在函数/窗体的尾部位置时,该方法将取消调用方。由于Clojure托管在JVM上,并且JVM不支持尾部调用优化,因此需要技巧来进行递归。
recur
形式就是这种技巧,它允许Clojure编译器执行类似于尾部调用优化的操作。另外,它验证recur
确实在尾部位置。好处是您可以确保优化确实发生。关于clojure - Clojure:要重发的尾部位置到底是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7813497/