我是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
不在函数的尾部?
答案 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
的测试中,循环绑定count
和m
与全局变量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中使用递归时,它更简单。