clojure中的reduce-while函数?

时间:2013-12-05 08:18:00

标签: clojure

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

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

  2. 如果总和非常大,我不在乎确切的总和。例如,如果数字之和大于9999,我只返回10000而不总计剩余数字。

  3. 如果我使用某些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惯用方法来解决这个问题吗?我认为该解决方案可以应用于需要类似逻辑的一组问题。

    任何评论都表示赞赏。感谢。

2 个答案:

答案 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版本。