习惯性的Clojure方式找到seq中最常见的项目

Eam*_*ain 5 idiomatic clojure

给定一系列项目,我想找到n个最频繁的项目,按频率降序排列.所以例如我希望这个单元测试通过:

(fact "can find 2 most common items in a sequence"
      (most-frequent-n 2 ["a" "bb" "a" "x" "bb" "ccc" "dddd" "dddd" "bb" "dddd" "bb"]) 
      =>
      '("bb" "dddd"))
Run Code Online (Sandbox Code Playgroud)

我是Clojure的新手,仍然试图掌握标准库.这是我想出的:

(defn- sort-by-val [s]        (sort-by val s))
(defn- first-elements [pairs] (map #(get % 0) pairs))

(defn most-frequent-n [n items]
  "return the most common n items, e.g. 
     (most-frequent-n 2 [:a :b :a :d :x :b :c :d :d :b :d :b])  => 
         => (:d :b)"
  (take n (->
           items               ; [:a :b :a :d :x :b :c :d :d :b :d :b]
           frequencies         ; {:a 2, :b 4, :d 4, :x 1, :c 1}
           seq                 ; ([:a 2] [:b 4] [:d 4] [:x 1] [:c 1])
           sort-by-val         ; ([:x 1] [:c 1] [:a 2] [:b 4] [:d 4])
           reverse             ; ([:d 4] [:b 4] [:a 2] [:c 1] [:x 1])
           first-elements)))   ; (:d :b :a :c :x)
Run Code Online (Sandbox Code Playgroud)

然而,这似乎是一个复杂的函数链,可以进行相当常见的操作.是否有更优雅或更惯用(或更有效)的方式来做到这一点?

mik*_*era 8

正如您所发现的,通常您会使用sort-by和frequency的组合来获取频率排序列表.

(sort-by val (frequencies ["a" "bb" "a" "x" "bb" "ccc" "dddd" "dddd" "bb" "dddd" "bb"]))
=> (["x" 1] ["ccc" 1] ["a" 2] ["dddd" 3] ["bb" 4])
Run Code Online (Sandbox Code Playgroud)

然后你可以相当容易地操纵它来获得最低/最高频率的项目.也许是这样的:

(defn most-frequent-n [n items]
  (->> items
    frequencies
    (sort-by val)
    reverse
    (take n)
    (map first)))
Run Code Online (Sandbox Code Playgroud)

这与您的解决方案非常类似(除了您不需要巧妙使用->>宏的辅助函数).

总的来说,我认为你的解决方案非常好.不要担心功能链 - 它实际上是一个非常简短的解决方案,对于逻辑上相当复杂的概念.尝试在C#/ Java中编写相同的东西,你会看到我的意思......

  • `(反向(by-by f coll))`由于没有真正的原因而非常昂贵 - 更喜欢`(sort-by(comp-f)coll)`.另外,关于你是否使用`first`和`second`或`key`和`val`,我会保持一致,因为它们与地图条目相同. (5认同)