如何在clojure中匹配阵列大小?

如何在clojure中匹配阵列大小?,clojure,Clojure,我想写一个函数,返回给定列表中可被3整除的所有数字之和。我不知道如何根据列表的大小进行模式匹配。当我运行下面的代码时,我得到一个nil指针异常 我试图使用第一原理来实现这一点,而不是使用循环映射和减少 (defn sum3 [[head & tail]] (if (= (mod head 3) 0) (+ head (sum3 tail)) (sum3 tail)) [[head]] (if (= (mod head 3) 0) head

我想写一个函数,返回给定列表中可被3整除的所有数字之和。我不知道如何根据列表的大小进行模式匹配。当我运行下面的代码时,我得到一个nil指针异常

我试图使用第一原理来实现这一点,而不是使用
循环
映射
减少

(defn sum3
  [[head & tail]]
  (if (= (mod head 3) 0)
    (+ head (sum3 tail))
    (sum3 tail))
  [[head]]
  (if (= (mod head 3) 0)
    head
    0))


(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println (sum3 [1 2 3])))

首先,让我们使用
reduce
执行版本:

(defn sum3 [coll]
  (reduce (fn [sum el]
            (if (zero? (mod el 3))
              (+ sum el)
              sum)) 0 coll))
注意
zero?
的用法。这里不需要模式匹配

然后我们可以继续使用递归性:

(defn sum3
  ([coll] (sum3 0 coll))
  ([sum [head & tail]]
   (let [add (if (zero? (mod head 3))
               (+ sum head)
               sum)]
     (if tail
       (recur add tail)
       add))))
同样,没有模式匹配(您必须使用
core.match
,因为clojure不使用模式匹配)。仅使用参数的数量进行分派。这就是函数语法无效的原因:在这两种情况下,它都是1-arity函数。注意使用
recur
递归函数。它只能用于尾部递归

我们可以使其成为1-arity函数,而无需分派参数:

(defn sum3
  [[head & tail]]
  (let [m3? (zero? (mod head 3))]
    (if (seq tail)
      (if m3?
        (+ head (sum3 tail))
        (sum3 tail))
      (if m3? head 0))))
注意,这个版本不是尾部递归的,对于大的输入将溢出


但实际上
reduce
版本,甚至
循环
版本看起来都要好得多。

事实上,主要错误在于
defn
语法。这里没有定义两个arity,而是先定义一个arity
[[head]]]
,然后是body,这是完全有效的clojure代码。要进行两个算术运算,应将每个算术运算放在括号中:

(defn sum3
  ([[head]]
   (if (= (mod head 3) 0)
     head
     0))
  ([[head & tail]]
   (if (= (mod head 3) 0)
     (+ head (sum3 tail))
     (sum3 tail))))
但它也会失败 因为您没有定义不同的算术,它们是相等的(一个参数调用),所以编译器会产生错误。Clojure解构根本不是模式匹配。这只是从集合中检索项目的一种方法。但您可以通过一个小改动来修复此问题(模拟某种长度模式匹配):

user> 
(defn sum3
  ([head]
   (if (= (mod head 3) 0)
     head
     0))
  ([head & tail]
   (if (= (mod head 3) 0)
     (+ head (apply sum3 tail))
     (apply sum3 tail))))
#'user/sum3

user> (sum3 1 2 3 4 5 6)
9

user> (apply sum3 [1 2 3 4 5 6])
9
现在你有了两个不同的算术的公平变量


但是是的,它不是尾部递归的,所以在现实生活中,您可以使用
reduce
loop/recur
clojure中的函数分派是根据函数的参数数量而不是函数调用中的解构值数量来完成的

一旦选择了适当的arity,并且函数开始运行,就会进行分解,并将符号绑定到分解后的值

幸运的是,clojure以so的形式提供了任意自定义函数分派,如果您希望根据可以分派的参数长度进行分派的话。以你的例子来说,这虽然不是特别难,但也太过分了。在其他一些情况下,这是有意义的

此函数的常规、单算术方法如下所示:

user> (defn sum3
       [[head & tail]]
       (if (seq tail)
         (if (= (mod head 3) 0)
           (+ head (sum3 tail))
           (sum3 tail))
         (if (= (mod head 3) 0)
           head
           0)))
#'user/sum3
user> (sum3 [1 2 3 4 5 6])
9
一般来说,您应该始终使用递归而不是直接递归,尽管对于这个问题,在演示这个原理时这不是一个太大的问题。

(sum3())
对于这个实现来说是一个问题。