【问题标题】:"Can only recur from tail position, compiling" error while using recur in loop在循环中使用 recur 时出现“只能从尾部位置重复,正在编译”错误
【发布时间】:2015-02-14 02:37:14
【问题描述】:

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

【问题讨论】:

  • 仅供参考,您可以写 (defn numbers_semi_random [] ...) 而不是 (def numbers_semi_random (fn [] ...))
  • 是的,但是会抛出同样的错误。当我在文档中的 recur 部分看到时,我尝试练习的那部分代码。谢谢

标签: clojure functional-programming clojure-repl


【解决方案1】:

您的意图似乎很可能通过使用when 代替while 来实现(然后您的循环将继续打印Xn 的更新值,直到满足条件),但要解释异常:

从编译器的角度来看,问题在于虽然while 形式确实在您的loop 的尾部位置,但while 宏的扩展使得while 中没有任何内容相对于while 表单,body 处于尾部位置。 (如果主体在while 形式的尾部位置,它也将在封闭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 不在尾部位置,这就是编译器所抱怨的。

【讨论】:

    【解决方案2】:

    尾部位置是在离开作用域之前需要评估的最后一个表单。使用loop 时,recur 语句必须能够使用新的绑定重新开始,无需对前一个循环进行进一步的计算。

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

    也许您打算将recur 调用置于while 之外?但是循环绑定Xncountwhile 测试中的全局变量m 的比较让我觉得这里还有更多需要解开的东西。这里要多少个嵌套循环?

    【讨论】:

    • 非常抱歉,我看到了我的错误,我没有看到文档中有关特殊表格的所有信息:clojure.org/special_forms,非常感谢您的帮助。
    • 同意第一段,但 recur 不是“在循环尾部的尾部”——如果是,那么它实际上将在尾部位置 w.r.t.循环。 (如果 X 是 Y 内最后要做的事情,Y 是 Z 内要做的最后一件事,那么 X 是 Z 内要做的最后一件事。)之所以不是,是因为 while' 的形状s 扩展,我发布了一个答案来更详细地解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-07
    • 1970-01-01
    • 2016-08-25
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多