Shuffling many collections by folding over a numeric range

Often, I find myself folding over a range object if I need to repeatedly transform something. This, to me though, feels like an abuse of fold since I never even use the numbers that I'm folding with.

Almost perfect example:

(ns shuffle-example
(:import [java.util Collection Collections]))

(defn shuffle [^Collection coll rand-gen]
(let [j-coll (java.util.ArrayList. coll)]
(Collections/shuffle j-coll rand-gen)
(into (empty coll) j-coll)))

(defn shuffle-many [^Collection coll n-shuffles rand-gen]
(reduce (fn [c _] (shuffle c rand-gen))
coll
(range n-shuffles)))

1. Is this considered bad form? Is there a more idiomatic way of achieving this? I know in this case it's inefficient since it requires constant conversions between list types, but it shows my question well.
2. Is there a better way to deterministically shuffle a list in Clojure? Clojure comes with a shuffle function, but it doesn't accept a Random, so it's not very useful. Short of writing my own shuffle from scratch, this is the best I've been able to come up with.

If you want to transform a value v by applying a function f n times, it may be clearer to use a combination of iterate and nth:

(nth (iterate f v) n)


If you want to write a 'good' general-purpose implementation of shuffle that accepts a random generator, I suggest you simply adapt the implementation in clojure.core. You'll see the implementation you proposed is actually pretty close!

Regarding performance, keep in mind that if you want to shuffle a coll n times, nothing will beat the performance of repeatedly shuffling a mutable collection in place.

• You're right, iterate + nth is significantly cleaner. Thanks. – Carcigenicate Oct 11 '16 at 15:42

I ended up adding the following to my personal library:

(defn iterate-many [coll repetitions iterate-f]
(nth
(iterate iterate-f coll)
repetitions))


It's already proven useful. My shuffle-many can then be reduced to:

(defn shuffle-many [^Collection coll n-shuffles rand-gen]
(iterate-many coll n-shuffles #(shuffle % rand-gen)))


As @Valentin pointed out, since my shuffle involves a conversion from/to Java collections, it's far from efficient, but it makes for a simple example.