Dada una secuencia de elementos, quiero encontrar los n artículos más frecuentes, en orden descendente de frecuencia. Así, por ejemplo, me gustaría que esta prueba de la unidad pase a:forma idiomática de Clojure para encontrar los artículos más frecuentes en una secuencia
(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"))
Soy bastante nuevo en Clojure y todavía tratando de llegar a la adherencia con la biblioteca estándar. Esto es lo que se me ocurrió:
(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)
Sin embargo, esto parece una complicada cadena de funciones para hacer una operación bastante común. ¿Hay una manera más elegante o más idiomática (o más eficiente) de hacer esto?
Gracias Mikera, su solución es una buena mejora. (1) Veo cómo usar las macros de flecha correctamente para evitar la necesidad de funciones auxiliares. (2) 'sort-by' puede trabajar directamente en el resultado de' frequencies' sin requerir hacer 'seq' primero. (3) Hay una función 'first' en la biblioteca estándar, así que no necesito crear la mía. (4) Hacer el 'take' antes del' map' es probablemente más eficiente. –
'(reverse (sort-by f coll))' es tremendamente costoso sin una razón real, prefiera en su lugar '(sort-by (comp - f) coll)'. Además, sería coherente con respecto a si usa 'first' y' second' o 'key' y' val', ya que son equivalentes para las entradas de mapas. – amalloy