5

一連のアイテムが与えられた場合、頻度の高い順に 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)

ただし、これはかなり一般的な操作を行うための複雑な関数のチェーンのように見えます。これを行うためのより洗練された、またはより慣用的な (またはより効率的な) 方法はありますか?

4

1 に答える 1

9

あなたが発見したように、通常、並べ替えと頻度の組み合わせを使用して、頻度で並べ替えられたリストを取得します。

(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 で同じことをコーディングしてみてください。

于 2012-09-30T00:37:06.310 に答える