在程序世界中,如果我必须找到符合测试的列表的第一项,我只会使用break
或return
。
在Clojure中,当我使用reduce
处理列表来查找第一个值时,如果我继续并处理整个列表,那么效率会不高吗?
例如:验证字典列表中的错误;每个字典都有一个名为count
的密钥。现在,列表中这些计数字段的总和不应超过某个值。如何在列表中找到总和超出限制的第一项?
理想情况下,我会使用reduce
并维持一个总计;一旦总数超过限额,我想停在那里(我无法弄明白该怎么做)。
此外,reduce的返回值将是每次到现在的总和,但我需要在所有结束时返回索引。
答案 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}]]