为什么recur期望使用%&和一个短形式函数的一个参数

时间:2012-05-11 14:53:01

标签: clojure

注意:不是Why does Clojure recur think it should only have one argument?的副本。我没有使用循环。

(def t 
  #(let [[low high] (sort %&)] {:low low :high h}))

(t 3 2)
=> {:low 2, :high 3}

鉴于这是按预期工作的。怎么没有:

(def t 
  #(let [[low high] (sort %&)] 
    (if (= 0 low)
      nil
      (do
        (println {:low low :high high})
        (recur low (dec high))))))

(t 3 2)
=> java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2

鉴于它说它期待1个参数我可以猜测我可以通过将参数转换为集合来使其工作:

(def t
  #(let [[low high] (sort %&)]
    (if (= 0 low)
      nil
      (do
        (println {:low low :high high})
        (recur [low (dec high)])))))

(t 3 2)
=> {:low 2, :high 3}
   {:low 2, :high 2}
   {:low 1, :high 2}
   {:low 1, :high 1}
   nil

......但为什么?

1 个答案:

答案 0 :(得分:6)

这就是它的设计方式。 Clojure website说:

  

recur表达式必须与递归点的arity完全匹配。特别是,如果递归点是可变参数fn方法的顶部,则不会收集rest args - 应传递单个seq(或null)。

我认为它是以这种方式设计的,因为如果函数本身给你一个序列(而不是单独的参数),那么复制形式更自然地接受一个序列,或者可以成为序列的东西。如果不是这种情况,那么您需要拆分给定的序列以执行递归。

你的例子似乎不适合模具,因为看起来你真的只关心有两个参数,这意味着你真的不需要其余的参数。你最好明确定义两个参数,并在let语句中确定哪个是低和高,而不是对其余的args进行排序并对它们进行解构。

这是您的代码,只需要很少的修改。我将两个显式参数包装在一个向量中,然后将它们传递给sort(基本上模仿rest args),并将两个参数传递给recur。

(def t
  #(let [[low high] (sort [%1 %2])]
    (if (= 0 low)
      nil
      (do
        (println {:low low :high high})
        (recur low (dec high))))))

然而,在保持重复形式的同时,我可能会稍微重构一下:

(defn t [x y]
  (let [low (min x y) high (max x y)]
    (when-not (zero? low)
      (println {:low low :high high})
      (recur low (dec high)))))