【问题标题】:accounting for duplicates in a custom clojure vector计算自定义 clojure 向量中的重复项
【发布时间】:2016-04-15 14:20:21
【问题描述】:

我正在尝试提出一种功能解决方案来解决由交替集和数值组成的自定义数据结构中的重复项。

一个例子:

(def a [#{:a} 0.1 #{:b} 0.3 #{:a :b} 0.1 #{:a} 0.3 #{:b} 0.1 #{:a} 0.1])

我想添加对应于重复集的值以产生

[#{:a} 0.5 #{:b} 0.4 #{:a :b} 0.1]

我可以使用loop/recur 做到这一点,但想知道是否有办法在 Clojure 中使用高阶函数。

谢谢。

【问题讨论】:

  • 你能分享你的循环/递归解决方案吗?
  • 对的顺序重要吗?如果没有,您可以将数据保存为从一组关键字到数字序列(向量?)的映射。

标签: vector clojure


【解决方案1】:
(reduce
  (fn [acc [k v]]
    (update acc k (fnil + 0) v))
  {}
  (partition 2 a))

首先,使用partition 和reduce 将您的序列划分为键值对。诀窍是使用fnil,它将nil 替换为尚未添加到acc 的键值0。

这会给你一张地图:

{#{:a} 0.5, #{:b} 0.4, #{:b :a} 0.1}

如果您需要它作为平面值序列,您可以将其传递给seq 和flatten:

(->> a
  (partition 2)
  (reduce
    (fn [acc [k v]]
      (update acc k (fnil + 0) v))
    {})
  (apply concat)
  (into []))

;; => (#{:a} 0.5 #{:b} 0.4 #{:b :a} 0.1)

【讨论】:

  • 我以前从未使用过fnil。谢谢你指出这一点。非常优雅。
  • 不客气 :)。我忽略了您需要一系列值,而不是地图。我已经更新了我的答案。
  • 我知道我没有将您的答案标记为“已接受”,但确实如此。希望我可以将两者都标记为已接受。我做了一个(into [] (seq (flatten) ...) 在最后得到一个向量。
  • flatten 通常是个坏主意。在这种情况下(只有一个深度级别),最好使用(apply concat) 或(mapcat identity) 而不是(flatten)。它还可以让您从那里删除 (seq) 行
  • @leetwinski 你是对的,让我加强我的回答。
【解决方案2】:

还有一个变种(只是为了好玩,因为reduce版本显然有更好的性能):

(->> a
     (partition 2)
     (group-by first)
     (map (fn [[k v]] [k (apply + (map second v))]))
     (reduce into []))

【讨论】:

    【解决方案3】:

    我会将源向量折叠成一个映射,其中的键用作数值的聚合,然后将其展开回一个向量。

    (->> a
         (partition 2)
         (reduce (fn [acc [k v]]
                   (if (get acc k)
                     (update acc k (partial + v))
                     (assoc acc k v)))
                 {})
         (reduce (fn [acc [k v]]
                   (into acc [k v]))
                 []))
    

    【讨论】:

      猜你喜欢
      • 2017-12-23
      • 2014-02-27
      • 1970-01-01
      • 2019-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-29
      相关资源
      最近更新 更多