如何在Clojure的if条件中调用recur?

时间:2011-08-13 17:44:47

标签: clojure

我正在尝试在4clojure.com解决计算序列的练习。练习是在不使用count函数的情况下计算集合中元素的数量。

我以为我可以通过递归使用rest来做到这一点。如果我得到的不是空的,我会返回1 + recur on the sequence rest returned。问题是,我最终得到了

java.security.PrivilegedActionException: java.lang.UnsupportedOperationException:
Can only recur from tail position

即使我正在调用recur作为最后一句话。

(fn [coll] (let [tail (rest coll)]
             (if (empty tail)
               1
               (+ 1 (recur tail)))))

我错过了什么吗?

2 个答案:

答案 0 :(得分:8)

最后一个声明是添加,而不是对recur的调用,这就是为什么它不起作用。它在if里面的事实与它无关。 (fn [coll] (let [tail (rest coll)] (+ 1 (recur tail))))也无效。

将这样的函数转换为尾递归函数的常用方法是使函数接受第二个参数,该参数保存累加器以获得您正在累加的值,然后像这样递归:{{1}而不是尝试将{1}添加到(recur tail (+ acc 1))

作为一般规则:如果你对recur的结果做任何事情(例如添加1),它就不能处于尾部位置,所以它不起作用。

答案 1 :(得分:6)

你得到的错误是指出(+ 1 (recur tail))的最终表达式不是尾部调用优化可优化的(是一个单词吗?)。问题是它需要在堆栈上保留一堆(+ 1 ...)表达式,以便评估函数的结果。只有当被调用函数的值是 only 事物需要知道调用函数的返回时,才会发生尾调用优化。

您要写的内容几乎是fold。在这种情况下,函数应该传递集合的其余部分以及到目前为止的计数。

(fn [count coll] (let [tail (rest coll)]
    (if (empty tail)
        count
        (recur (+ 1 count) tail)))))