以下是frequencies
中clojure
的实施:
(defn frequencies
"Returns a map from distinct items in coll to the number of times
they appear."
[coll]
(persistent!
(reduce (fn [counts x]
(assoc! counts x (inc (get counts x 0))))
(transient {}) coll)))
assoc!
是否被视为突变?
assoc!
内frequencies
的复杂性是多少?
此外,似乎counts
在每次迭代中被访问两次:它是否会导致性能下降?
答案 0 :(得分:4)
assoc!
是一个瞬态的变异,我认为它是O(log n)摊销的。因此frequencies
的整个执行是O(n log n)。
counts
是一个局部绑定变量,因此访问它两次都没问题。
以下是不使用任何多重状态的频率的功能版本:
(defn frequencies-2 [coll]
(reduce (fn [m v] (assoc m v (inc (get m v 0)))) {} coll))
此功能版本也是O(n log n),但由于创建和丢弃更多临时对象,它会有更多的开销(更高的常数因子)。
答案 1 :(得分:2)
您可以使用树将地图从元素存储到具有log(n)复杂度的频率(它可以是二叉搜索树,AVL,红黑树等)。
选择此树的功能实现,即您不能改变它,而是assoc counts x freq
返回一个新的数据结构,在memomry中共享与counts
的公共部分。这是一种“写作复制”。
然后计算所有频率的性能将是O(n log(n))。
答案 2 :(得分:1)
assoc!
会改变瞬态数据,其性能远远优于assoc
。它并不违反不可变的Clojure模型(见http://clojure.org/transients)。
persistent!
和transient
为O(1)assoc!
是O(log32 n),它实际上是O(1),因为hash-map
的上限是~2 ^ 32项的大小,这使得最大树深度为6 因此,frequencies
的复杂性与coll
的大小呈线性关系。
备注:正如@mikera所注意到的,frequencies
的复杂性也与assoc
呈线性关系,但具有更高的常数因子。