我在Clojure的早期教訓之一是,當循環是正確的解決方案時使用循環並沒有錯。原始代碼不錯,不一定需要任何改進。
如果你有很多的權重(大小),二進制搜索正確的時間間隔會比簡單的線性搜索更好。用Clojure進行二進制搜索的方式大概是讓Java做到這一點。
(defn find-interval [intervals x]
(Math/abs (inc (java.util.Collections/binarySearch intervals x))))
學習一個重要的功能語法是關閉或「讓lambda」。我們可以通過將變量放在返回函數的詞彙環境中來保存變量。在這種情況下,我們將保存預先計算的累計總和w
,總計n
,以及我們想從矢量中選擇的值。
(defn weighted-choice-generator [choices]
(let [weights (java.util.ArrayList. (reductions + (map second choices)))
values (mapv first choices)
n (last weights)]
(fn [] (nth values (find-interval weights (long (rand n)))))))
我們將強制採樣數據爲值權重對的序列如上預期,從加權選擇發生器獲得我們的擊球功能。
(def hit-hobbit-asym (weighted-choice-generator
(map (juxt identity :size) asym-hobbit-body-parts)))
現在測試幾千次確認的點擊率是按比例大小:
(pprint (frequencies (repeatedly 59000 hit-hobbit-asym)))
{{:name "left-shoulder", :size 3} 2951,
{:name "chest", :size 10} 9922,
{:name "left-forearm", :size 3} 3046,
{:name "left-lower-leg", :size 3} 3038,
{:name "neck", :size 2} 1966,
{:name "back", :size 10} 9900,
{:name "left-ear", :size 1} 997,
{:name "nose", :size 1} 1023,
{:name "left-thigh", :size 4} 4020,
{:name "left-achilles", :size 1} 972,
{:name "left-hand", :size 2} 2075,
{:name "left-foot", :size 2} 2062,
{:name "left-eye", :size 1} 1047,
{:name "left-knee", :size 2} 2068,
{:name "left-upper-arm", :size 3} 2996,
{:name "abdomen", :size 6} 6020,
{:name "head", :size 3} 2933,
{:name "left-kidney", :size 1} 986,
{:name "mouth", :size 1} 978}
所謂的麪包和函數式編程的黃油'地圖,降低/ fold'約消耗整個序列的價值觀,他們沒有辦法「擺脫循環」(如果他們有這樣的選擇,他們會採取一個額外的參數,這將是一個決定什麼時候爆發的預測)。 – Ankur
在Clojure中,reduce有一種方法可以打破「簡化」的功能。 – noisesmith