一旦达到所需的累积,如何停止处理列表的reduce函数?

时间:2014-05-23 06:20:46

标签: clojure functional-programming

在程序世界中,如果我必须找到符合测试的列表的第一项,我只会使用breakreturn

在Clojure中,当我使用reduce处理列表来查找第一个值时,如果我继续并处理整个列表,那么效率会不高吗?

例如:验证字典列表中的错误;每个字典都有一个名为count的密钥。现在,列表中这些计数字段的总和不应超过某个值。如何在列表中找到总和超出限制的第一项?

理想情况下,我会使用reduce并维持一个总计;一旦总数超过限额,我想停在那里(我无法弄明白该怎么做)。

此外,reduce的返回值将是每次到现在的总和,但我需要在所有结束时返回索引。

2 个答案:

答案 0 :(得分:16)

您可以使用reduced功能终止缩减:

(reduce (fn [sum x] 
          (if (> sum 10) 
            (reduced 10) 
            (+ sum x))) 
        0 
        [1 2 3 4 5 6 7 8 9 10])

答案 1 :(得分:2)

我很想使用reductions而不是reduce来找到断点。这比@Jonas's solution慢很多,但允许我们练习一些序列库。

例如,给出

(def data [{:count 19}
           {:count 93}
           {:count 89}
           {:count 91}
           {:count 55}
           {:count 22}
           {:count 22}
           {:count 22}
           {:count 40}
           {:count 89}])

然后

(reductions + (map :count data))
; (19 112 201 292 347 369 391 413 453 542)

...返回累积:count值的序列。

我们假设限制为300。然后

(let [acceptable (take-while
                   (partial >= 300)
                   (reductions + (map :count data)))]
  acceptable)
; (19 112 201 292)

...返回符合限制标准的初始子序列。这告诉我们两件事我们想知道:

  • last元素是累计的:count
  • 它的长度 - 由函数count引起的,令人困惑地返回 - 是第一个拒绝元素从0开始的位置。

例如,您可以使用长度将原始序列拆分为已接受和拒绝的部分:

(let [acceptable (take-while ...)]
  (split-at (count acceptable) data))
; [({:count 19} {:count 93} {:count 89} {:count 91})
;  ({:count 55} {:count 22} {:count 22} {:count 22} {:count 40} {:count 89})]

如果我们知道原始序列是向量,我们可以使用subvec更快地提取其部分。

(let [acceptable (take-while ...)
      point (count acceptable)]
  [(subvec data 0 point) (subvec data point)])
; [[{:count 19} {:count 93} {:count 89} {:count 91}]
;  [{:count 55} {:count 22} {:count 22} {:count 22} {:count 40} {:count 89}]]