我应该使用
(apply + (filter prime? (range 1 20)))
或
(reduce + (filter prime? (range 1 20)))
编辑:这是优化工具包中的素数来源。
(defn prime? [n]
(cond
(or (= n 2) (= n 3)) true
(or (divisible? n 2) (< n 2)) false
:else
(let [sqrt-n (Math/sqrt n)]
(loop [i 3]
(cond
(divisible? n i) false
(< sqrt-n i) true
:else (recur (+ i 2)))))))
答案 0 :(得分:18)
如果您在性能方面提出要求,reduce
会更好一点:
(time (dotimes [_ 1e6] (apply + (filter even? (range 1 20)))))
"Elapsed time: 9059.251 msecs"
nil
(time (dotimes [_ 1e6] (reduce + (filter even? (range 1 20)))))
"Elapsed time: 8420.323 msecs"
nil
在这种情况下差异大约为7%,但YMMV取决于机器。
您尚未提供prime?
函数的来源,因此我已将even?
替换为谓词。请注意,您的运行时可能由prime?
占主导地位,在这种情况下,reduce
和apply
之间的选择更为重要。
如果你问哪个更“lispy”那么我会说reduce
实现是可取的,因为你正在做的是函数编程意义上的减少/折叠。
答案 1 :(得分:13)
我认为reduce
在可用时更可取,因为apply
使用列表作为函数的参数,但当你有一个大数字 - 比如说,一百万 - 在列表中的元素中,您将构造一个带有一百万个参数的函数调用!这可能会导致Lisp的某些实现出现一些问题。
答案 2 :(得分:8)
(reduce op ...)是规范和(应用op ...)异常(特别是对于str和concat)。
答案 3 :(得分:7)
我希望申请实现一个可能很难看的懒惰列表,而且你永远不想假设你的列表不是懒惰的,因为你可能突然发现自己被大量的内存使用所困扰。
Reduce将逐个抓住它们并将结果汇总到一个整体中,而不是立即将整个列表记录下来。
答案 4 :(得分:6)
我将扮演魔鬼的拥护者并争辩apply
。
reduce
是对fold
(更确切地说是foldl
)的左侧折叠,并且通常使用初始元素定义,因为折叠操作有两部分:< / p>
初始(或“零”)值
组合两个值的操作
为了找到一组数字的总和,使用+
的自然方式为(fold + 0 values)
,或者在clojure中(reduce + 0 values)
。
这明确地显示了空列表的结果,这很重要,因为在这种情况下+
返回0
对我来说并不明显 - 毕竟,+
是< em>二元运算符(所有fold
需要或假设)。
现在,实际上,事实证明,clojure的+
被定义为 more 而不是二元运算符。它将需要许多甚至零值。凉。但如果我们使用这个“额外”信息,那么向读者发出信号是友好的。 (apply + values)
这样做 - 它说“我以一种奇怪的方式使用+,不仅仅是二元运算符”。这有助于人们(至少我)理解代码。
[有趣的是,为什么apply
感觉更清楚。我认为部分原因是你要对读者说:“看,+
旨在接受多个值(这是适用的用途),因此语言实现将包括零值的情况。” reduce
应用于单个列表时不存在隐式参数。]
或者,(reduce + 0 values)
也没关系。但是(reduce + values)
在我身上引发了一种本能的反应:“嗯,+
提供零吗?”。
如果您不同意,那么请在您投票或发布回复之前,确定关于(reduce * values)
将返回哪个空列表?