"Can only recur from tail position, compiling" 在循环中使用 recur 时出错

"Can only recur from tail position, compiling" error while using recur in loop

我是 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是不是在函数的尾部?

尾部位置是在离开作用域之前需要计算的最后一个形式。使用 loop 时,recur 语句必须能够使用新的绑定重新开始,之前循环中没有进一步的计算需要完成。

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

也许您打算让 recur 调用在 while 之外?但是在 while 的测试中,循环绑定 Xncount 与全局变量 m 的比较让我觉得这里还有更多需要解决的问题。您想要多少个嵌套循环?

您的意图似乎可以通过使用 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 不在尾部位置,这就是编译器所抱怨的。