优化我们对标准Clojure 频率功能
的实施(defn my_frequencies_1 [coll]
(reduce (fn [counts x]
(assoc counts x (inc (get counts x 0))))
{} coll))
我们可以使用瞬变
(defn my_frequencies_2 [coll]
(persistent!
(reduce (fn [counts x]
(assoc! counts x (inc (get counts x 0))))
(transient {}) coll)))
或者减速器。
(defn my_frequencies_3 [coll]
(r/fold
(fn combinef
([] {})
([coll1 coll2] (merge-with + coll1 coll2)))
(fn reducef [coll x]
(assoc coll x (inc (get coll x 0))))
coll))
但是如果我们尝试使用两种机制
(defn my_frequencies_4 [coll]
(persistent!
(r/fold
(fn combinef
([] (transient {}))
([coll1 coll2] (transient
(merge-with
+
(persistent! coll1)
(persistent! coll2)))))
(fn reducef [coll x]
(assoc! coll x (inc (get coll x 0))))
coll)))
(with-out-str (time (do (my_frequencies_4 data) nil)))
我们遇到了问题。
java.lang.IllegalAccessError: null
(Unknown Source) sun.reflect.GeneratedConstructorAccessor2.newInstance
(Unknown Source) sun.reflect.DelegatingConstructorAccessorImpl.newInstance
(Unknown Source) java.lang.reflect.Constructor.newInstance
(Unknown Source) java.util.concurrent.ForkJoinTask.getThrowableException
(Unknown Source) java.util.concurrent.ForkJoinTask.reportResult
(Unknown Source) java.util.concurrent.ForkJoinTask.join
(Unknown Source) java.util.concurrent.ForkJoinPool.invoke
reducers.clj:49 clojure.core.reducers/fjinvoke
reducers.clj:336 clojure.core.reducers/foldvec
reducers.clj:357 clojure.core.reducers/eval10225[fn]
reducers.clj:81 clojure.core.reducers/eval10096[fn]
reducers.clj:98 clojure.core.reducers/fold
reducers.clj:96 clojure.core.reducers/fold
M:\clojure\reducers.clj:63 user/my-frequencies-4
M:\clojure\reducers.clj:79 user/eval10803
我认为这是因为当时只有一个线程有权处理可变数据结构,即瞬态映射。是否有可能在最后的Clojure代码中使用经典的Java同步原语来规避这个问题?