clojure - 在 seq 中查找最频繁项的惯用 Clojure 方法

标签 clojure idioms

给定一个项目序列,我想按照频率的降序找到 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"))

我对 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)

然而,这似乎是一个复杂的函数链来执行一个相当常见的操作。有没有更优雅或更惯用(或更有效)的方法来做到这一点?

最佳答案

正如您所发现的,通常您会使用排序依据和频率的组合来获得频率排序列表。

(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])

然后你可以很容易地操纵它来获得最低/最高频率的项目。也许是这样的:
(defn most-frequent-n [n items]
  (->> items
    frequencies
    (sort-by val)
    reverse
    (take n)
    (map first)))

这再次与您的解决方案非常相似(除此之外,您不需要巧妙使用 ->> 宏的辅助函数)。

所以总的来说,我认为你的解决方案非常好。不要担心函数链 - 对于逻辑上相当复杂的概念,它实际上是一个非常简短的解决方案。尝试用 C#/Java 编写同样的代码,你就会明白我的意思......

关于clojure - 在 seq 中查找最频繁项的惯用 Clojure 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12657566/

相关文章:

clojure - 为什么我不能使用正则表达式来验证字符串作为映射键?

java - 检查 null 时惯用的 Kotlin

http - Clojure 跨域错误 - 完全丢失

java - 使用 Java 对象作为 Clojure 映射

function - Clojure - 自动将映射条目转换为函数参数的语法糖?

Kotlin 的 'let' 加上 elvis,以及意外的 null 返回值

rust - 什么是函数签名和类型?

python - 设置 matplotlib 图形/轴属性的首选方法

c++ - 搜索参数空间时避免嵌套 for 循环

clojure - 避免 Clojure DSL 中的名称冲突