2

楽しみのために、私はPeterNorvigのUdacityCS212コース(Pythonで教えられています)をClojureを学ぶための手段として使用しています。

このコースでは、指定された頻度で発生するシーケンスの最初の要素を返す関数があります。

def kind(n, ranks):
    """Return the first rank that this hand has
    exactly n-of-a-kind of. Return None if there
    is no n-of-a-kind in the hand."""
    for r in ranks:
        if ranks.count(r) == n: return r
    return None

私はこれをclojureのワンライナーとして行う方法を理解しましたが、それはなんと恐ろしいワンライナーです:

(defn n-of-kind
  [n ranks]
  "Detect whether a hand rank contains n of a kind, returning first
  rank that contains exactly n elements"
  (first (keys (into {} (filter #(= (second %) n) (frequencies ranks))))))

(n-of-kind 3 [5 5 5 3 3]) ;; correctly returns 5

私の直感は、より良い方法がなければならないということです。頻度関数は非常に便利ですが、このコードの残りの部分は、値を検索してそのキーを返すだけです。頻度関数が値ではなく頻度をキーとしてマップを返した場合、((頻度ランク)n)のようなことを行うことができます。

誰かがこれを行うためのより読みやすい/簡潔な方法を提案できますか?

4

2 に答える 2

2

別のバージョン

(defn n-of-kind [n ranks]
  (first (filter #(= n (count (filter #{%} ranks)))
                 ranks)))
于 2013-01-03T17:49:56.187 に答える
1

値をキーとして使用し、キーを値として使用して新しいマップを作成することにより、マップを反転できます。

(zipmap (vals my-map) (keys my-map))

周波数からキーへのマップを使用することは非常に簡単で、元の問題を解決しますが、アイテムの2番目のカウントが最初のカウントを覆い隠すため、同じ回数発生するアイテムが消えるという問題が発生します。

user> (def data (take 20 (repeatedly #(rand-nth [:a :b :c :d :e :f]))))
#'user/data
user> (let [f (frequencies data)] (zipmap (vals f) (keys f)))
{1 :f, 3 :d, 4 :c}
user> (frequencies data)
{:e 4, :b 4, :a 4, :d 3, :c 4, :f 1}

代わりに、頻度からその頻度のキーのセットへのマップのリストから始めて、それをセットの単一のマップに減らすと、コードは少し大きくなりますが、データが失われることはありません。

user> (reduce (partial merge-with clojure.set/union) 
        (let [f (frequencies data)] 
          (map hash-map (vals f) (map hash-set (keys f)))))

{1 #{:f}, 3 #{:d}, 4 #{:a :c :b :e}}
于 2013-01-03T18:56:03.803 に答える