【问题标题】:Performant way to group elements in a collection into new collections with Clojure使用 Clojure 将集合中的元素分组到新集合中的高效方法
【发布时间】:2015-09-17 15:51:11
【问题描述】:

我有一个包含数万个元素的集合(Java 列表),我正在编写一个 Clojure 函数,该函数需要根据谓词将此列表分成几个部分。最后,我有几个 Clojure 集合,其中只有与集合关联的谓词匹配的元素。

以下代码解决了我的问题,但对输入列表进行了 3 次迭代。有没有更好的方法来做到这一点?

(defn divide-into-groups [col]
  (let [one (filter #(< % 3) col)
        two (filter #(and (>= % 3) (< % 6)) col)
        three (filter #(>= % 6) col)]
   [one two three]))
(divide-into-groups (shuffle (range 10)))
;[(2 0 1) (4 3 5) (6 8 7 9)]

我真的在寻找一个实用的 Clojure 解决方案。我已经知道我可以创建三个集合作为 vars 并在 divide-into-groups 函数中改变它们,也许这就是 Clojure 的方式。如果是,请说出来。

(注意:我上面使用的谓词不是我的生产代码中的谓词。我正在使用的数据也不是数字。这只是一个 SSCCE。这个问题的答案必须适用于一般问题集合中有任意数据和任意谓词。当然,性能很好。要清楚的是,filter 返回的惰性列表将全部被完全迭代并用于生成一些输出。所以我不能依赖惰性解决方案;- )

【问题讨论】:

  • 是否保证完全其中一个函数总是正确的?
  • 是的,其中一个函数总是正确的。

标签: clojure


【解决方案1】:

这就是group-by 的用途。除了谓词之外,您唯一需要的是给每个谓词组一个“名称”来指示它将在哪个组中:

(defn divide-into-groups [xs]
  (let [group (fn [x] (cond (>= x 6) :large
                            (>= 6 x 3) :medium
                            :else :small))]
    (group-by group xs)))

user> (divide-into-groups (shuffle (range 10)))
{:small [1 2 0], :large [6 9 8 7], :medium [3 4 5]}

【讨论】:

    【解决方案2】:

    您可以使用partition-by[1]。

    (partition-by (fn [x] (cond (< x 3) :coll-1 
                          (and (>= x 3) (< x 6)) :coll-2 
                          (>= x 6) :coll-3)) 
                  (range 10))
    

    可以从谓词函数序列以编程方式构造所需的函数。唯一值,即:coll-1:coll-2等可以是任意值,甚至是谓词在序列中的索引。

    编辑:

    ;; updated to use map-indexed and some-fn as suggested by @Andre
    
    (defn partitions
      [preds coll]
      (let [party-fn (apply some-fn 
                            (map-indexed (fn [idx pred] 
                                           #(when (pred %1) idx))
                                         preds))]
        (partition-by party-fn coll)))   
    
    ;; output
    (partitions [ #(< %1 3) #(<= 3 %1 5) #(>= %1 6)] (range 10))
    ((0 1 2) (3 4 5) (6 7 8 9))
    

    [1] - https://clojuredocs.org/clojure.core/partition-by

    【讨论】:

    • 可以使用map-indexed + some-fn 更成功地完成。但我认为这是一个很好的解决方案。
    • @Andre,花了一点时间了解some-fn 做了什么:)。谢谢你的建议。我已经更新了我的答案。
    • 非常感谢你们花时间写下这些答案。不幸的是,它们没有解决问题,因为集合没有排序,并且谓词可能与排序顺序没有任何关系。使用(shuffle (range 10)) 尝试您的解决方案,看看它们是否产生正确的结果。如果是这样,那么我可以投票,否则这不是一个正确的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-03
    • 2012-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-05
    相关资源
    最近更新 更多