Introduce Sequence.shuffled

#KT-37751
This commit is contained in:
Ilya Gorbunov
2020-03-26 22:06:59 +03:00
parent f42d9eefa1
commit e58f1c8932
3 changed files with 91 additions and 2 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -8,6 +8,8 @@
package kotlin.sequences
import kotlin.random.Random
/**
* Given an [iterator] function constructs a [Sequence] that returns values through the [Iterator]
* provided by that function.
@@ -109,6 +111,36 @@ public fun <T, R> Sequence<Pair<T, R>>.unzip(): Pair<List<T>, List<R>> {
return listT to listR
}
/**
* Returns a sequence that yields elements of this sequence randomly shuffled.
*
* Note that every iteration of the sequence returns elements in a different order.
*
* The operation is _intermediate_ and _stateful_.
*/
@SinceKotlin("1.4")
public fun <T> Sequence<T>.shuffled(): Sequence<T> = shuffled(Random)
/**
* Returns a sequence that yields elements of this sequence randomly shuffled
* using the specified [random] instance as the source of randomness.
*
* Note that every iteration of the sequence returns elements in a different order.
*
* The operation is _intermediate_ and _stateful_.
*/
@SinceKotlin("1.4")
public fun <T> Sequence<T>.shuffled(random: Random): Sequence<T> = sequence<T> {
val buffer = toMutableList()
while (buffer.isNotEmpty()) {
val j = random.nextInt(buffer.size)
val last = @OptIn(ExperimentalStdlibApi::class) buffer.removeLast()
val value = if (j < buffer.size) buffer.set(j, last) else last
yield(value)
}
}
/**
* A sequence that returns the values from the underlying [sequence] that either match or do not match
* the specified [predicate].
@@ -1,10 +1,11 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package test.collections
import kotlin.random.Random
import kotlin.test.*
fun fibonacci(): Sequence<Int> {
@@ -633,6 +634,60 @@ public class SequenceTest {
assertEquals(listOf("act", "wast", "test"), sequenceOf("act", "test", "wast").sortedWith(comparator).toList())
}
@Test fun shuffled() {
val sequence = (0 until 100).asSequence()
val originalValues = sequence.toList()
val shuffled = sequence.shuffled()
val values1 = shuffled.toList()
val values2 = shuffled.toList()
assertNotEquals(originalValues, values1)
assertNotEquals(values1, values2, "Each run returns new shuffle")
assertEquals(originalValues.toSet(), values1.toSet())
assertEquals(originalValues.toSet(), values2.toSet())
assertEquals(originalValues.size, values1.distinct().size)
assertEquals(originalValues.size, values2.distinct().size)
}
@Test fun shuffledPredictably() {
val list = List(10) { it }
val sequence = list.asSequence()
val shuffled1 = sequence.shuffled(Random(1))
val shuffled2 = sequence.shuffled(Random(1))
val values1 = shuffled1.toList()
val values2 = shuffled2.toList()
assertEquals(values1, values2)
assertEquals("[5, 3, 7, 9, 8, 2, 6, 0, 4, 1]", values1.toString())
val values1n = shuffled1.toList()
assertNotEquals(values1, values1n, "Each run returns new shuffle")
val values42 = sequence.shuffled(Random(42)).toList()
assertEquals("[3, 6, 7, 1, 8, 2, 9, 4, 0, 5]", values42.toString())
}
@Test fun shuffledPartially() {
val countingRandom = object : Random() {
var counter: Int = 0
override fun nextBits(bitCount: Int): Int {
counter++
return Random.nextBits(bitCount)
}
}
val sequence = (0 until 100).asSequence()
val partialShuffle = sequence.shuffled(countingRandom).take(10)
assertEquals(0, countingRandom.counter)
val result = partialShuffle.toList()
assertEquals(10, result.size)
assertEquals(10, countingRandom.counter)
}
@Test fun associateWith() {
val items = sequenceOf("Alice", "Bob", "Carol")
val itemsWithTheirLength = items.associateWith { it.length }
@@ -4714,6 +4714,8 @@ public final class kotlin/sequences/SequencesKt {
public static final fun scanReduceIndexed (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function3;)Lkotlin/sequences/Sequence;
public static final fun sequence (Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence;
public static final fun sequenceOf ([Ljava/lang/Object;)Lkotlin/sequences/Sequence;
public static final fun shuffled (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence;
public static final fun shuffled (Lkotlin/sequences/Sequence;Lkotlin/random/Random;)Lkotlin/sequences/Sequence;
public static final fun single (Lkotlin/sequences/Sequence;)Ljava/lang/Object;
public static final fun single (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun singleOrNull (Lkotlin/sequences/Sequence;)Ljava/lang/Object;