2012-05-11 33 views
4

的短格式func的一个参数注意:不是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 

.. 。 但为什么?

回答

6

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

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

我相信它被设计成这样,因为如果函数本身是给你一个序列(而不是单独的参数),那么这将是更自然的易复发的形式,而不是接受一个序列,或东西,可以成为一个序列。如果情况并非如此,那么您需要拆分给定的序列以执行递归。

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

这是您的代码,只需最少的修改。我将这两个显式参数包装在一个向量中,然后将它们传递给sort(本质上是模仿rest参数),并将两个参数传递给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))))) 
+0

+1的重构版本。 – Gert

+0

为了好玩,我创建了一个没有重复发生的懒惰版本:https://www.refheap.com/paste/2691 – Jeremy

+0

感谢您的回答。我查了一下函数定义的特殊格式,但没有想到再次发现。该代码只是一个简单的例子,它做了一些事情。这个问题在代码高尔夫中出现,我只是想知道为什么这样呢? – status203

相关问题