Clojure中的reduce-while函数是什么?

10

我是Clojure编程的新手,想知道以下事情的惯用方式:

  1. 我想要对一个数字集合nums求和,其中可能包含大量数字,假设这些数字都为正数。

  2. 如果数字总和非常大,我不关心确切的总和。例如,如果数字总和大于9999,我将简单地返回10000,而根本不会对其余数字求和。

如果我使用一些面向对象的语言(如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 的东西,但找不到。是否有类似于:

(reduce-while pred f val coll)

还有其他Clojure惯用的方法来解决这个问题吗?我认为这种解决方案可以应用于一组需要类似逻辑的问题。

欢迎任何评论。谢谢。

2个回答

20

如果您正在使用Clojure 1.5.x,那么您可以利用新的reduced函数

(reduce #(if (> %1 9999) (reduced 10000) (+ %1 %2)) nums)

谢谢。这正是我正在寻找的。我正在4clojure.com上做一些练习,但它似乎还不支持Clojure 1.5 :( - nybon

11

稍为鲜为人知的 Clojure 函数之一似乎是 reductions。它会给出您计算过程中的所有中间结果:

(reductions + (range 4)) ;; => (0 1 3 6)
(reduce + (range 4))     ;; => 6
< p >reductions 的结果序列的最后一个元素将是归约值。有多种方法可以强制执行你的谓词,例如使用 some

(let [sums (reductions + nums)]
  (if (some #(> % 9999) sums)
    10000
    (last sums)))

@leonid-beschastny提供的reduce/reduced版本可能更快(没有惰性序列开销,规约器等...),但这个版本也适用于早期的Clojure版本。


1
关于第一个代码块 - 值应该是(0 1 3 6)和6。 - DoubleWord

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接