Question

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 :-)

Was it helpful?

Solution

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;
        }
    };
};

OTHER TIPS

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

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

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top