Provide constrainOnce extension method for the Sequence.
Sequence created from the generator function without an initial value can be iterated only once. The same is for BufferedReader.lines sequence.
This commit is contained in:
@@ -280,8 +280,7 @@ public class TakeSequence<T>(private val sequence: Sequence<T>,
|
||||
private val count: Int
|
||||
) : Sequence<T> {
|
||||
init {
|
||||
if (count < 0)
|
||||
throw IllegalArgumentException("count should be non-negative, but is $count")
|
||||
require (count >= 0) { throw IllegalArgumentException("count should be non-negative, but is $count") }
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> = object : Iterator<T> {
|
||||
@@ -361,8 +360,7 @@ public class DropSequence<T>(private val sequence: Sequence<T>,
|
||||
private val count: Int
|
||||
) : Sequence<T> {
|
||||
init {
|
||||
if (count < 0)
|
||||
throw IllegalArgumentException("count should be non-negative, but is $count")
|
||||
require (count >= 0) { throw IllegalArgumentException("count should be non-negative, but is $count") }
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> = object : Iterator<T> {
|
||||
@@ -510,11 +508,26 @@ private class GeneratorSequence<T: Any>(private val getInitialValue: () -> T?, p
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a wrapper sequence that provides values of this sequence, but ensures it can be iterated only one time.
|
||||
*
|
||||
* [IllegalStateException] is thrown on iterating the returned sequence from the second time.
|
||||
*/
|
||||
public fun <T> Sequence<T>.constrainOnce(): Sequence<T> {
|
||||
// as? does not work in js
|
||||
//return this as? ConstrainedOnceSequence<T> ?: ConstrainedOnceSequence(this)
|
||||
return if (this is ConstrainedOnceSequence<T>) this else ConstrainedOnceSequence(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sequence which invokes the function to calculate the next value on each iteration until the function returns `null`.
|
||||
*
|
||||
* Returned sequence is constrained to be iterated only once.
|
||||
*
|
||||
* @see constrainOnce
|
||||
*/
|
||||
public fun <T : Any> sequence(nextFunction: () -> T?): Sequence<T> {
|
||||
return GeneratorSequence(nextFunction, { nextFunction() })
|
||||
return GeneratorSequence(nextFunction, { nextFunction() }).constrainOnce()
|
||||
}
|
||||
|
||||
deprecated("Use sequence() instead")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
package kotlin
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
|
||||
private class ConstrainedOnceSequence<T>(sequence: Sequence<T>) : Sequence<T> {
|
||||
private val sequenceRef = AtomicReference(sequence)
|
||||
|
||||
override fun iterator(): Iterator<T> {
|
||||
val sequence = sequenceRef.getAndSet(null) ?: throw IllegalStateException("This sequence can be consumed only once.")
|
||||
return sequence.iterator()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,9 +246,9 @@ public inline fun <T> Reader.useLines(block: (Sequence<String>) -> T): T =
|
||||
*
|
||||
* We suggest you try the method [useLines] instead which closes the stream when the processing is complete.
|
||||
*
|
||||
* @return a sequence of corresponding file lines
|
||||
* @return a sequence of corresponding file lines. The sequence returned can be iterated only once.
|
||||
*/
|
||||
public fun BufferedReader.lines(): Sequence<String> = LinesSequence(this)
|
||||
public fun BufferedReader.lines(): Sequence<String> = LinesSequence(this).constrainOnce()
|
||||
|
||||
deprecated("Use lines() function which returns Sequence<String>")
|
||||
public fun BufferedReader.lineIterator(): Iterator<String> = lines().iterator()
|
||||
|
||||
@@ -20,24 +20,24 @@ fun fibonacci(): Sequence<Int> {
|
||||
public class SequenceTest {
|
||||
|
||||
test fun filterEmptySequence() {
|
||||
val stream = sequenceOf<String>()
|
||||
assertEquals(0, stream.filter { false }.count())
|
||||
assertEquals(0, stream.filter { true }.count())
|
||||
val sequence = sequenceOf<String>()
|
||||
assertEquals(0, sequence.filter { false }.count())
|
||||
assertEquals(0, sequence.filter { true }.count())
|
||||
}
|
||||
|
||||
test fun mapEmptyStream() {
|
||||
val stream = sequenceOf<String>()
|
||||
assertEquals(0, stream.map { false }.count())
|
||||
assertEquals(0, stream.map { true }.count())
|
||||
test fun mapEmptySequence() {
|
||||
val sequence = sequenceOf<String>()
|
||||
assertEquals(0, sequence.map { false }.count())
|
||||
assertEquals(0, sequence.map { true }.count())
|
||||
}
|
||||
|
||||
test fun requireNoNulls() {
|
||||
val stream = sequenceOf<String?>("foo", "bar")
|
||||
val notNull = stream.requireNoNulls()
|
||||
val sequence = sequenceOf<String?>("foo", "bar")
|
||||
val notNull = sequence.requireNoNulls()
|
||||
assertEquals(listOf("foo", "bar"), notNull.toList())
|
||||
|
||||
val streamWithNulls = sequenceOf("foo", null, "bar")
|
||||
val notNull2 = streamWithNulls.requireNoNulls() // shouldn't fail yet
|
||||
val sequenceWithNulls = sequenceOf("foo", null, "bar")
|
||||
val notNull2 = sequenceWithNulls.requireNoNulls() // shouldn't fail yet
|
||||
fails {
|
||||
// should throw an exception as we have a null
|
||||
notNull2.toList()
|
||||
@@ -128,9 +128,9 @@ public class SequenceTest {
|
||||
}
|
||||
|
||||
test fun plus() {
|
||||
val stream = sequenceOf("foo", "bar")
|
||||
val streamCheese = stream + "cheese"
|
||||
assertEquals(listOf("foo", "bar", "cheese"), streamCheese.toList())
|
||||
val sequence = sequenceOf("foo", "bar")
|
||||
val sequenceCheese = sequence + "cheese"
|
||||
assertEquals(listOf("foo", "bar", "cheese"), sequenceCheese.toList())
|
||||
|
||||
// lets use a mutable variable
|
||||
var mi = sequenceOf("a", "b")
|
||||
@@ -141,8 +141,8 @@ public class SequenceTest {
|
||||
test fun plusCollection() {
|
||||
val a = sequenceOf("foo", "bar")
|
||||
val b = listOf("cheese", "wine")
|
||||
val stream = a + b
|
||||
assertEquals(listOf("foo", "bar", "cheese", "wine"), stream.toList())
|
||||
val sequence = a + b
|
||||
assertEquals(listOf("foo", "bar", "cheese", "wine"), sequence.toList())
|
||||
|
||||
// lets use a mutable variable
|
||||
var ml = sequenceOf("a")
|
||||
@@ -154,7 +154,7 @@ public class SequenceTest {
|
||||
}
|
||||
|
||||
|
||||
test fun iterationOverStream() {
|
||||
test fun iterationOverSequence() {
|
||||
var s = ""
|
||||
for (i in sequenceOf(0, 1, 2, 3, 4, 5)) {
|
||||
s = s + i.toString()
|
||||
@@ -162,31 +162,51 @@ public class SequenceTest {
|
||||
assertEquals("012345", s)
|
||||
}
|
||||
|
||||
test fun streamFromFunction() {
|
||||
test fun sequenceFromFunction() {
|
||||
var count = 3
|
||||
|
||||
val stream = sequence {
|
||||
val sequence = sequence {
|
||||
count--
|
||||
if (count >= 0) count else null
|
||||
}
|
||||
|
||||
val list = stream.toList()
|
||||
val list = sequence.toList()
|
||||
assertEquals(listOf(2, 1, 0), list)
|
||||
|
||||
fails {
|
||||
sequence.toList()
|
||||
}
|
||||
}
|
||||
|
||||
test fun streamFromFunctionWithInitialValue() {
|
||||
test fun sequenceFromFunctionWithInitialValue() {
|
||||
val values = sequence(3) { n -> if (n > 0) n - 1 else null }
|
||||
val expected = listOf(3, 2, 1, 0)
|
||||
assertEquals(expected, values.toList())
|
||||
assertEquals(expected, values.toList(), "Iterating sequence second time yields the same result")
|
||||
}
|
||||
|
||||
|
||||
test fun makeSequenceOneTimeConstrained() {
|
||||
val sequence = sequenceOf(1, 2, 3, 4)
|
||||
sequence.toList()
|
||||
sequence.toList()
|
||||
|
||||
val oneTime = sequence.constrainOnce()
|
||||
oneTime.toList()
|
||||
assertTrue("should fail with IllegalStateException") {
|
||||
fails {
|
||||
oneTime.toList()
|
||||
} is IllegalStateException
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun <T, C : MutableCollection<in T>> Sequence<T>.takeWhileTo(result: C, predicate: (T) -> Boolean): C {
|
||||
for (element in this) if (predicate(element)) result.add(element) else break
|
||||
return result
|
||||
}
|
||||
|
||||
test fun streamExtensions() {
|
||||
test fun sequenceExtensions() {
|
||||
val d = ArrayList<Int>()
|
||||
sequenceOf(0, 1, 2, 3, 4, 5).takeWhileTo(d, { i -> i < 4 })
|
||||
assertEquals(4, d.size())
|
||||
|
||||
Reference in New Issue
Block a user