我正在努力学习Clojure,所以我认为开始的一个好方法是使用它来完成Euler项目的挑战,第一个挑战是将1000以下的所有数字相加,可以被3或5整除
我原来的代码是:
(defn div3 [input-no] (zero? (mod input-no 3)))
(defn div5 [input-no] (zero? (mod input-no 5)))
(reduce + (filter (or div3 div5) (range 1 1000)))
但是这没有用,结果是过滤器只返回一个可被3整除的数字列表,而不是那些被5整除的数字。
我将代码更改为:
(defn div3or5 [input-no] (or (div3 input-no) (div5 input-no)))
(reduce + (filter div3or5 (range 1 1000)))
哪个得到了正确的结果,但我不知道为什么我的原始代码不起作用。
有人可以对此有所了解吗?
答案 0 :(得分:4)
您遇到的问题是filter
期望谓词(一个输入并返回true或false的函数)作为其第一个参数。但是,虽然div3
和div5
是函数,但您不能简单地将它们与or
结合使用。您需要构造一个新函数,该函数接受一个参数并将其提供给div3
和div5
,并调用or
和两者的结果。
幸运的是,在Clojure中这很容易做到,试试
(filter #(or (div3 %) (div5 %)) (range1 1000))
#()
是定义函数内联的简写(也称为 lambda ),你可以使用%1
到第二个%2
的第一个参数} 等等。如果只有一个参数,那么您可以%
使用%1
see this question。
您可能还想了解#()
只是fn
形式的语法糖
看起来像这样:(fn [arg1 arg2 ... & restArgs] (forms))
。 #()
有一些限制(例如,它不能嵌套)。
答案 1 :(得分:3)
如果您只是评估REPL中的(or div3 div5)
,您可以看到发生了什么:
=> (or div3 div5)
#<user$div3 user$div3@73305c>
也就是说,or
正在评估函数div3
(filter
正在使用它,给出您描述的行为。)
原因是or
将返回其第一个非虚假参数(即第一个参数不是nil
或false
);在这种情况下,参数是两个函数对象,函数对象不是nil
或false
。
换句话说,or
发生在函数本身,而不是函数的结果。正如Paul所说,你可以使用匿名函数使or
对结果而不是函数本身起作用。