clojure中的reduce-while函数?

nyb*_*bon 7 clojure

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

  1. 我想总结一组数字nums,其中可能包含大量数字,我们假设只有正数.

  2. 如果总和非常大,我不在乎确切的总和.例如,如果数字的总和大于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;
}
Run Code Online (Sandbox Code Playgroud)

Clojure中的一个简单实现可能如下所示:

(let [sum (reduce + nums)]
   (if (> sum 9999) 10000 sum))
Run Code Online (Sandbox Code Playgroud)

但是,这似乎浪费了一些CPU资源来汇总整个数字集合,这是不希望的.我正在寻找像take-while函数但是为了减少,但找不到它.是否有类似的东西:

(reduce-while pred f val coll)
Run Code Online (Sandbox Code Playgroud)

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

任何评论表示赞赏.谢谢.

Leo*_*tny 16

如果您使用的是Clojure 1.5.x,那么您可以利用reduced功能:

(reduce #(if (> %1 9999) (reduced 10000) (+ %1 %2)) nums)
Run Code Online (Sandbox Code Playgroud)


xsc*_*xsc 10

其中一个鲜为人知的Clojure功能似乎是reductions.它将为您提供计算的所有中间结果:

(reductions + (range 4)) ;; => (0 1 2 3)
(reduce + (range 4))     ;; => 3
Run Code Online (Sandbox Code Playgroud)

reductions'结果seq 的最后一个元素将是减少的值.有多种方法可以强制执行谓词,例如some:

(let [sums (reductions + nums)]
  (if (some #(> % 9999) sums)
    10000
    (last sums)))
Run Code Online (Sandbox Code Playgroud)

@ leonid-beschastny给出的reduce/ reduced版本可能更快(没有懒惰的序列开销,缩减器......),但是这个版本也适用于早期的Clojure版本.