GeneratorSequence: do not get first value, until it's requested by iterator, allow initialValue to be null and return EmptySequence in this case.
#KT-9153 Fixed
This commit is contained in:
@@ -473,29 +473,28 @@ private class DistinctIterator<T, K>(private val source : Iterator<T>, private v
|
|||||||
|
|
||||||
private class GeneratorSequence<T: Any>(private val getInitialValue: () -> T?, private val getNextValue: (T) -> T?): Sequence<T> {
|
private class GeneratorSequence<T: Any>(private val getInitialValue: () -> T?, private val getNextValue: (T) -> T?): Sequence<T> {
|
||||||
override fun iterator(): Iterator<T> = object : Iterator<T> {
|
override fun iterator(): Iterator<T> = object : Iterator<T> {
|
||||||
var nextItem: T? = getInitialValue()
|
var nextItem: T? = null
|
||||||
var nextState: Int = if (nextItem == null) 0 else 1 // -1 for unknown, 0 for done, 1 for continue
|
var nextState: Int = -2 // -2 for initial unknown, -1 for next unknown, 0 for done, 1 for continue
|
||||||
|
|
||||||
private fun calcNext() {
|
private fun calcNext() {
|
||||||
nextItem = getNextValue(nextItem!!)
|
nextItem = if (nextState == -2) getInitialValue() else getNextValue(nextItem!!)
|
||||||
nextState = if (nextItem == null) 0 else 1
|
nextState = if (nextItem == null) 0 else 1
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun next(): T {
|
override fun next(): T {
|
||||||
if (nextState == -1)
|
if (nextState < 0)
|
||||||
calcNext()
|
calcNext()
|
||||||
|
|
||||||
if (nextState == 0)
|
if (nextState == 0)
|
||||||
throw NoSuchElementException()
|
throw NoSuchElementException()
|
||||||
val result = nextItem as T
|
val result = nextItem as T
|
||||||
// Clean next to avoid keeping reference on yielded instance
|
// Do not clean nextItem (to avoid keeping reference on yielded instance) -- need to keep state for getNextValue
|
||||||
// need to keep state
|
|
||||||
// nextItem = null
|
|
||||||
nextState = -1
|
nextState = -1
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasNext(): Boolean {
|
override fun hasNext(): Boolean {
|
||||||
if (nextState == -1)
|
if (nextState < 0)
|
||||||
calcNext()
|
calcNext()
|
||||||
return nextState == 1
|
return nextState == 1
|
||||||
}
|
}
|
||||||
@@ -530,7 +529,10 @@ public fun <T : Any> sequence(nextFunction: () -> T?): Sequence<T> {
|
|||||||
*
|
*
|
||||||
* The sequence can be iterated multiple times, each time starting with the [initialValue].
|
* The sequence can be iterated multiple times, each time starting with the [initialValue].
|
||||||
*/
|
*/
|
||||||
public /*inline*/ fun <T : Any> sequence(initialValue: T, nextFunction: (T) -> T?): Sequence<T> =
|
public fun <T : Any> sequence(initialValue: T?, nextFunction: (T) -> T?): Sequence<T> =
|
||||||
|
if (initialValue == null)
|
||||||
|
EmptySequence
|
||||||
|
else
|
||||||
GeneratorSequence({ initialValue }, nextFunction)
|
GeneratorSequence({ initialValue }, nextFunction)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package test.collections
|
package test.collections
|
||||||
|
|
||||||
import org.junit.Test as test
|
import org.junit.Test as test
|
||||||
|
import org.junit.Test
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -233,6 +234,24 @@ public class SequenceTest {
|
|||||||
assertEquals(expected, values.toList(), "Iterating sequence second time yields the same result")
|
assertEquals(expected, values.toList(), "Iterating sequence second time yields the same result")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun sequenceFromFunctionWithLazyInitialValue() {
|
||||||
|
var start = 3
|
||||||
|
val values = sequence({ start }, { 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")
|
||||||
|
|
||||||
|
start = 2
|
||||||
|
assertEquals(expected.drop(1), values.toList(), "Initial value function is called on each iterator request")
|
||||||
|
|
||||||
|
// does not throw on construction
|
||||||
|
val errorValues = sequence<Int>({ (throw IllegalStateException()) }, { null })
|
||||||
|
// does not throw on iteration
|
||||||
|
val iterator = errorValues.iterator()
|
||||||
|
// throws on advancing
|
||||||
|
assertFails { iterator.next() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@test fun sequenceFromIterator() {
|
@test fun sequenceFromIterator() {
|
||||||
val list = listOf(3, 2, 1, 0)
|
val list = listOf(3, 2, 1, 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user