Ceylon equivalent of Collections.shuffle()

265 views Asked by At

Is there a way to "shuffle" up an Iterable (or Sequence), such that elements are subsequently ordered randomly, similar to Java's Collections.shuffle()? I've looked in the API docs for Iterable, Collection, and Sequence, but haven't found anything relevant. (Side note: ceylon.language::shuffle is confusingly named)

I could implement a shuffle myself I suppose, but I'm busy trying to be lazy :-)

3

There are 3 answers

2
gdejohn On BEST ANSWER

I also went looking for this and couldn't find it. Here's an implementation:

import ceylon.collection {ArrayList}
import ceylon.math.float {random}

"Don't interleave multiple iterators!"
Iterable<Element, Absent> shuffle<Element, Absent>(Iterable<Element, Absent> elements)
        given Absent satisfies Null => object satisfies Iterable<Element, Absent> {
    value list = ArrayList{elements = elements;};
    iterator() => object satisfies Iterator<Element> {
        variable value index = list.size;
        shared actual Element|Finished next() {
            value randomIndex = (random() * index--).integer;
            if (exists element = list[index]) {
                assert (exists randomElement = list[randomIndex]);
                list.set(index, randomElement);
                list.set(randomIndex, element);
                return randomElement;
            }
            return finished;
        }
    };
};
2
Max On

I ended up implementing my own, based on the last "inside-out" algorithm here.

[Element*] shuffle<Element>({Element*} input) {
    value newList = LinkedList<Element>();
    for(el in input){
        value j = math.randomInteger {lowerBound=0; upperBound=newList.size; inclusiveUpperBound=true;};
        if(j == newList.size){
            newList.add(el);
        } else {
            value elementToMove = newList[j];
            assert(exists elementToMove);
            newList.add(elementToMove);
            newList.set(j, el);
        }
    }
    return newList.sequence;
}

Haven't verified correctness yet. I implemented math.randomInteger too, which you can probably guess at the implementation.

0
gdejohn On

The SDK now includes the module ceylon.random with a randomize function:

List<Elements> randomize<Elements>({Elements*} elements)