【问题标题】:How to stop a reduce function from processing the list once the desired accumulation has been reached?一旦达到所需的累积,如何停止reduce函数处理列表?
【发布时间】:2014-05-23 06:20:45
【问题描述】:

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

在 Clojure 中,当我使用 reduce 处理列表以查找第一个值时,如果我继续处理整个列表会不会效率低下?

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

理想情况下,我会使用reduce 并保持运行总数;一旦总数超过限制,我想停在那里(我不知道该怎么做)。

此外,reduce 的返回值每次都是到目前为止的总和,但我需要在最后返回索引。

【问题讨论】:

    标签: clojure functional-programming


    【解决方案1】:

    您可以使用reduced 函数来终止归约:

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

    【讨论】:

    • 哇哦!存在这样的东西。但我需要返回发生这种情况的位置,否则返回-1。但是如果在reduce中返回索引,我如何累积总和。我应该将这两个值都放在地图或列表中吗?并在每个 reduce 调用结束时返回两个值?
    • 是的,您可以更新累加器中的索引。类似于 (fn [acc x] (if (> (:sum acc) 10) (reduced acc) (assoc acc :idx (inc (:idx acc)) :sum (+ (:sum acc) x)))。
    【解决方案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}]]
    

    【讨论】:

      猜你喜欢
      • 2020-04-05
      • 1970-01-01
      • 2020-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-03
      相关资源
      最近更新 更多