"只能从尾部位置重复,编译"使用recur in循环时出错

时间:2015-02-14 02:37:14

标签: clojure functional-programming clojure-repl

我是Clojure的新手,为了练习,我试图为半随机数应用一个简单的算法。

几天前,我读到了clojure文档中的循环,以及它们如何使用recur,所以我尝试用这行代码编写一个循环:

(def numbers_semi_random
  (fn []      
    (loop[Xn 4 count 0]                    
     (while (and (not (= Xn m)) (< count m))
       (println (mod (+ (* 5.0 Xn) 7.0) m))
       (recur (mod (+ (* 5.0 Xn) 7.0) m) (inc count))    
))))

但是当我执行代码时,会显示此错误

CompilerException java.lang.UnsupportedOperationException: Can only recur from tail position, compiling

发生了什么事? recur不在函数的尾部?

3 个答案:

答案 0 :(得分:6)

似乎可以通过使用when代替while来完成您的意图(您的循环将继续打印Xn的更新值,直到满足条件为止),但解释异常:

从编译器的角度来看,问题是虽然while形式确实处于与loop相关的尾部位置,但while宏的扩展是这样的, while的身体相对于while形式处于尾部位置。 (如果身体处于尾部位置w.r.t. while形式,它也将处于尾部位置w.r.t.封闭的loop。)

原因是while扩展为loop表达式并在用户提供的正文后提供自己的recur

(while test
  body...)

;; expands to

(loop []
  (when test
    body...
    (recur)))

因此,如果您提供recur作为while正文的一部分,那么您得到的是

(loop []
  (when test
    ...
    (recur ...) ; from user code
    (recur)))

此处,用户提供的(recur ...)与其立即封闭的loop不在尾部位置,这就是编译器抱怨的内容。

答案 1 :(得分:2)

尾部位置是离开范围之前需要评估的最后一种形式。使用loop时,recur语句必须能够使用新绑定重新开始,不需要进行前一个循环的进一步计算。

在您的代码中,while的尾部位置有一个loop块。您尝试调用recur的位置不在循环的尾部位置(尽管它位于尾部的尾部)。

也许您打算将recur来电置于while之外?但是,在Xn的测试中,循环绑定countm与全局变量while的比较让我觉得还有更多要解决的问题。你想在这里有多少个嵌套循环?

答案 2 :(得分:0)

你的问题在于你正在使用&#39;而在&#39;而不是&#39;当&#39;。虽然通常与&#39;做&#39;当你想发出副作用时

(def n (atom -33)
(while (neg? @n) (do (... something with side effect)) (swap! n inc))

您的代码的正确实现将是......

(def numbers_semi_random
  (fn [m]
    (loop[Xn 4 count 0]
     (when (and (not (= Xn m)) (< count m))
       (println (mod (+ (* 5.0 Xn) 7.0) m))
       (recur (mod (+ (* 5.0 Xn) 7.0) m) (inc count))))))

坚持&#39;当&#39;或者&#39;如果&#39;当在Clojure中使用递归时,它更简单。