我是Clojure编程的新手,想知道做以下事情的惯用方法是什么:
我想总结一组数字nums
,其中可能包含大量数字,我们假设只有正数。
如果总和非常大,我不在乎确切的总和。例如,如果数字之和大于9999,我只返回10000而不总计剩余数字。
如果我使用某些OO语言(如Java)实现它,我可能会像下面这样做:
private int sum(int[] nums) {
int sum = 0;
for(int n : nums) {
if(sum > 9999) {
sum = 10000;
break;
} else {
sum += n;
}
}
return sum;
}
Clojure中的一个天真的实现可能看起来像:
(let [sum (reduce + nums)]
(if (> sum 9999) 10000 sum))
然而,这似乎浪费了一些CPU资源来对整个数字集合求和,这是不希望的。我正在寻找像take-while函数但是为了减少,但找不到它。是否有类似的东西:
(reduce-while pred f val coll)
或者还有其他Clojure惯用方法来解决这个问题吗?我认为该解决方案可以应用于需要类似逻辑的一组问题。
任何评论都表示赞赏。感谢。
答案 0 :(得分:16)
如果您使用的是Clojure 1.5.x,那么您可以利用new reduced
function:
(reduce #(if (> %1 9999) (reduced 10000) (+ %1 %2)) nums)
答案 1 :(得分:10)
其中一个鲜为人知的Clojure函数似乎是reductions
。它将为您提供计算的所有中间结果:
(reductions + (range 4)) ;; => (0 1 2 3)
(reduce + (range 4)) ;; => 3
reductions
'结果seq的最后一个元素将是减少的值。有多种方法可以强制执行谓词,例如:使用some
:
(let [sums (reductions + nums)]
(if (some #(> % 9999) sums)
10000
(last sums)))
@ leonid-beschastny给出的reduce
/ reduced
版本可能更快(没有懒惰的序列开销,缩减器,......)但是这个版本也适用于早期的Clojure版本。