有没有办法“洗牌”一个Iterable
(或Sequence
),以便随后随机排序元素,类似于Java的Collections.shuffle()
?我查看了、 和的 API 文档Iterable
,但没有发现任何相关内容。(旁注:名称混淆)Collection
Sequence
ceylon.language::shuffle
我想我可以自己实施洗牌,但我正忙着偷懒:-)
有没有办法“洗牌”一个Iterable
(或Sequence
),以便随后随机排序元素,类似于Java的Collections.shuffle()
?我查看了、 和的 API 文档Iterable
,但没有发现任何相关内容。(旁注:名称混淆)Collection
Sequence
ceylon.language::shuffle
我想我可以自己实施洗牌,但我正忙着偷懒:-)
我也去找这个了,没找到。这是一个实现:
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;
}
};
};
SDK 现在包含ceylon.random
具有以下randomize
功能的模块:
List<Elements> randomize<Elements>({Elements*} elements)
我最终实现了我自己的,基于最后一个“由内而外”算法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;
}
还没有验证正确性。我也实现了 math.randomInteger ,你可能会猜到它的实现。