1
\$\begingroup\$

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.
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ You're right, iterate + nth is significantly cleaner. Thanks. \$\endgroup\$ Commented Oct 11, 2016 at 15:42
0
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.