我有一个函数,它从一个集合中获取n个随机项,使得相同的项永远不会被选中两次.我很容易完成这件事:
(defn take-rand [n coll]
(take n (shuffle coll)))
Run Code Online (Sandbox Code Playgroud)
但是我有一个令人讨厌的要求,即当提供相同的种子时,我需要返回相同的随机子集,即
(defn take-rand [n coll & [seed]] )
(take-rand 5 (range 10) 42L) ;=> (2 5 8 6 7)
(take-rand 5 (range 10) 42L) ;=> (2 5 8 6 7)
(take-rand 5 (range 10) 27L) ;=> (7 6 9 1 3)
(take-rand 5 (range 10)) ;=> (9 7 8 5 0)
Run Code Online (Sandbox Code Playgroud)
我有一个解决方案,但它感觉有点笨重,而不是非常惯用.任何Clojure老兵都可以提出改进(或完全不同的方法)吗?
这是我做的:
(defn take-rand
"Returns n randomly selected items from coll, or all items if there are fewer than n.
No item will be picked more than once."
[n coll & [seed]]
(let [n (min n (count coll))
rng (if seed (java.util.Random. seed) (java.util.Random.))]
(loop [out [], in coll, n n]
(if (or (empty? in) (= n 0))
out
(let [i (.nextInt rng n)]
(recur (conj out (nth in i))
(concat (take i in) (nthrest in (inc i)))
(dec n)))))))
Run Code Online (Sandbox Code Playgroud)
好吧,我不是老将,但是怎么样:
(defn shuffle-with-seed
"Return a random permutation of coll with a seed"
[coll seed]
(let [al (java.util.ArrayList. coll)
rnd (java.util.Random. seed)]
(java.util.Collections/shuffle al rnd)
(clojure.lang.RT/vector (.toArray al))))
(defn take-rand [n coll & [seed]]
(take n (if seed
(shuffle-with-seed coll seed)
(shuffle coll))))
Run Code Online (Sandbox Code Playgroud)
shuffle-with-seed与clojure类似shuffle,它只是将一个实例传递Random给Java java.util.Collections.shuffle().
用可shuffle重现的函数替换第一个解决方案中的函数。我将在下面向您展示我的解决方案:
(defn- shuffle'
[seed coll]
(let [rng (java.util.Random. seed)
rnd #(do % (.nextInt rng))]
(sort-by rnd coll)))
(defn take-rand'
([n coll]
(->> coll shuffle (take n)))
([n coll seed]
(->> coll (shuffle' seed) (take n))))
Run Code Online (Sandbox Code Playgroud)
我希望这个解决方案能产生你预期的结果:
user> (take-rand' 5 (range 10))
(5 4 7 2 6)
user> (take-rand' 5 (range 10))
(1 9 0 8 5)
user> (take-rand' 5 (range 10))
(5 2 3 1 8)
user> (take-rand' 5 (range 10) 42)
(2 6 4 8 1)
user> (take-rand' 5 (range 10) 42)
(2 6 4 8 1)
user> (take-rand' 5 (range 10) 42)
(2 6 4 8 1)
Run Code Online (Sandbox Code Playgroud)