我想编写一个函数来返回给定列表中可被3整除的所有数字的总和。我不知道如何根据列表的大小进行模式匹配。当我运行以下代码时,我得到一个nil指针异常。
我尝试使用第一原则,而不是使用loop
map
和reduce
(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])))
答案 0 :(得分:1)
clojure中的函数调度是在函数调用中对函数的参数个数而不是在结构化值的数量上完成的。
一旦选择了适当的arity并且函数已经开始运行,就会发生解构并将符号绑定到desctructured值。
Fortunatly clojure以multimethods的形式提供任意自定义函数调度,因此如果您想根据参数的长度进行调度。对于你的例子来说,虽然不是特别难,但它有点过分。在其他情况下,它是有道理的。
这个函数的正常单一方法看起来像这样:
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
一般来说,你应该总是使用复发而不是直接递归,但是对于这个问题,在演示这个校长时问题并不是太多。
答案 1 :(得分:0)
首先让我们使用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
版本 - 甚至loop
版本看起来要好得多。
答案 2 :(得分:0)
实际上主要错误是defn
语法。你不要在这里定义两个arities,而是制作一个arity [[head]]
然后进入body,这是完全有效的clojure代码。要制作2个arities,你应该把每个都放在括号中:
(defn sum3
([[head]]
(if (= (mod head 3) 0)
head
0))
([[head & tail]]
(if (= (mod head 3) 0)
(+ head (sum3 tail))
(sum3 tail))))
但它也会失败 因为你没有定义不同的arities,它们是相等的(一个参数调用),所以编译器会产生错误。 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
现在你有两个不同的arities的公平变体。
但是,是的,它不是尾递归,所以在现实生活中你会选择reduce
或loop/recur