重载函数失败,导致编译器递归错误

时间:2011-08-25 19:22:50

标签: recursion clojure

使用以下代码,尽管所有重复都在尾部位置,但我得到#<CompilerException java.lang.UnsupportedOperationException: Can only recur from tail position (NO_SOURCE_FILE:4)>。如果我从单参数版本中删除recur,它就会停止抱怨。为什么会这样?

(defn remove-duplicates "Removes duplicate elements of lst. 

For example, given (1 2 3 1 4 1 2), remove-duplicates returns a sequence 
containing the elements (1 2 3 4), in some order."
  [lst] (recur (rest lst) (set (first lst)))
  [lst uniques] (cond (zero? (count lst)) uniques
                      :else (cond
                              (some (partial = (first lst)) uniques)
                              (recur (rest lst) uniques)
                              :else 
                              (recur (rest lst) (first lst)))))

2 个答案:

答案 0 :(得分:4)

你没有正确地分割多个机构。应该阅读(defn foo ([x] (...)) ([x y] (...)))。这会导致编译器认为你做的事情完全不同,这可能是你的问题所在。

答案 1 :(得分:1)

首先:你知道你想要的只是(def remove-duplicates set)或 - 如果你想要一个矢量 - (def remove-duplicates-vec (comp vec set)),对吗?

这里有五件事:

  1. 当amalloy注意到,你应该添加parens
  2. 正如kotarak注意到的那样,你不能在arities之间重现
  3. 您无法致电(set (first lst)),因为set想要竞争。如果你愿意,可以做(set (vector (first [1 2 3 2 3])))之类的事情,但这既不美观也不惯用
  4. 可以简化(cond pred1 code1 :else (cond pred2a code2a :else code2b))(cond pred1 code1 pred2a code2a :else code2b) - 您所做的就是cond macro,就好像它是if一样(内置到目前为止)我知道)
  5. 你的最后一次尾巴调用也是错误的。假设我们从[1 2 3 2 1]开始
    1. 当你第一次打电话时,你有以下论点:([2 3 2 1] #{1})(我已经跳过了无聊的部分)
    2. 然后你的最后一个谓词是真的,所以你选择了([3 2 1] 2),这显然是错误的,因为你想要([3 2 1] #{1 2})。您可能想要致电(recur (rest lst) (conj uniques (first lst)))
  6. 总结:

    (defn remove-duplicates
      ([lst] (remove-duplicates (rest lst) #{(first coll)}))
      ([lst uniques]
        (cond
          (zero? (count lst)) uniques
          (some (partial = (first lst)) uniques)
          (recur (rest lst) uniques)
          :else 
          (recur (rest lst) (conj uniques (first lst))))))