unj*_*nj2 13 lisp scheme clojure
我应该用吗?
(apply + (filter prime? (range 1 20)))
Run Code Online (Sandbox Code Playgroud)
要么
(reduce + (filter prime? (range 1 20)))
Run Code Online (Sandbox Code Playgroud)
编辑:这是优化工具包中的素数来源.
(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)))))))
Run Code Online (Sandbox Code Playgroud)
ala*_*ode 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
Run Code Online (Sandbox Code Playgroud)
在这种情况下差异约为7%,但YMMV取决于机器.
您尚未提供prime?
函数的源代码,因此我已将其替换even?
为谓词.请记住,你的运行可由为主prime?
,在这种情况下之间的选择reduce
和apply
问题就更少了.
如果你问哪个更"lispy",那么我会说reduce
实现是可取的,因为你正在做的是在函数编程意义上的减少/折叠.
new*_*cct 13
我认为reduce
当它可用时会更好,因为apply
使用列表作为函数的参数,但是当你在列表中有一个大数字 - 比如一百万个元素时,你将构造一个函数调用百万论点!这可能会导致一些Lisp实现出现问题.
我希望申请实现一个可能很丑的懒惰列表,你永远不想假设你的列表不是懒惰,因为你可能会突然发现自己被大量的内存使用所打扰.
Reduce将逐个抓住它们并将结果整合到一个整体中,从不将整个列表一次性整理.
我将扮演魔鬼的拥护者并争辩apply
.
reduce
是clojure的fold
(更确切地说foldl
)是左折,并且通常用初始元素定义,因为折叠操作有两个部分:
初始(或"零")值
用于组合两个值的操作
所以要找到一组数字的总和,自然的使用方式+
是(fold + 0 values)
或者,在clojure中(reduce + 0 values)
.
这显然显示了一个空列表的结果,这很重要,因为在这种情况下+
返回我并不明显0
- 毕竟,它+
是一个二元运算符(所有fold
需要或假设).
现在,在实践中,事实证明,Clojure的+
被定义为多比二进制运算符.它将需要许多甚至零值.凉.但如果我们使用这个"额外"信息,那么向读者发出信号是友好的. (apply + values)
这样做 - 它说"我以一种奇怪的方式使用+,不仅仅是二元运算符".这有助于人们(至少我)理解代码.
[有趣的是,为什么apply
感觉更清楚.而且我认为部分原因是你要对读者说:"看,+
被设计为接受多个值(这是适用的用途),因此语言实现将包括零值的情况." reduce
应用于单个列表时,隐式参数不存在.]
或者,(reduce + 0 values)
也没关系.但(reduce + values)
在我身上引发了一种本能的反应:"嗯,确实+
提供零?".
如果你不同意,那么请在你投票或发表回复之前,你确定什么(reduce * values)
会返回一个空列表?