0

How can I easily shuffle a list of tuples in sml? There doesn't seem to be any built in function to do so.

I assume I would need to use a random number generator but how you would move items in an list I have no idea.

user2864740
  • 60,010
  • 15
  • 145
  • 220

1 Answers1

0

This is for Moscow ML's Random library. For SML/NJ's Random library, you would have to adjust the random functions slightly.

val _ = load "Random"
val rng = Random.newgen ()

(* select([5,6,7,8,9], 2) = (7, [5,6,8,9]) *)
fun select (y::xs, 0) = (y, xs)
  | select (x::xs, i) = let val (y, xs') = select (xs, i-1) in (y, x::xs') end
  | select (_, i) = raise Fail ("Short by " ^ Int.toString i ^ " elements.")

(* Recreates a list in random order by removing elements in random positions *)
fun shuffle xs =
    let fun rtake [] _ = []
          | rtake ys max =
            let val (y, ys') = select (ys, Random.range (0, max) rng)
            in y :: rtake ys' (max-1)
            end
    in rtake xs (length xs) end
sshine
  • 15,635
  • 1
  • 41
  • 66
  • I tried modifying it to use the built in Random library, but I'm getting an error: uncaught exception Match [nonexhaustive match failure] which is on this line: | select (x::xs, i) = let val (y, xs') = select (xs, i-1) in (y, x::xs') end – lkjfsdf Dec 03 '14 at 09:36
  • When this happens, it means you have called `select(xs, i)` for an i >= length xs. When `Random.range (0, max) rng` is called, it generates a value in the interval [0,max) (i.e., not counting max itself). SML/NJ's Random.randRange uses an inclusive interval, which means you need to subtract 1 from max: `Random.randRange (0, max-1) rng`. Otherwise, the random number generator will once in a while generate a number that is one too big. – sshine Dec 03 '14 at 11:20