为了好玩,我正在使用Peter Norvig的Udacity CS212课程(在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)。
任何人都可以建议一种更可读/更简洁的方法吗?
答案 0 :(得分:2)
另一个版本
(defn n-of-kind [n ranks]
(first (filter #(= n (count (filter #{%} ranks)))
ranks)))
答案 1 :(得分:1)
您可以使用值作为键并将键作为值来构建新地图来反转地图:
(zipmap (vals my-map) (keys my-map))
使用从频率到键的映射很容易,并且解决了原始问题,尽管它遇到了相同次数的项目消失的问题,因为它们的第二次计数是第一次:
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}}