Sequences support improved (#358)

This commit is contained in:
Nikolay Igotti
2017-03-20 11:52:52 +03:00
committed by GitHub
parent c9d8d4d57d
commit 80041e6559
6 changed files with 1181 additions and 139 deletions
+260
View File
@@ -2462,3 +2462,263 @@ public fun CharArray.joinToString(separator: CharSequence = ", ", prefix: CharSe
return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun <T> Array<out T>.asIterable(): Iterable<T> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun ByteArray.asIterable(): Iterable<Byte> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun ShortArray.asIterable(): Iterable<Short> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun IntArray.asIterable(): Iterable<Int> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun LongArray.asIterable(): Iterable<Long> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun FloatArray.asIterable(): Iterable<Float> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun DoubleArray.asIterable(): Iterable<Double> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun BooleanArray.asIterable(): Iterable<Boolean> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates an [Iterable] instance that wraps the original array returning its elements when being iterated.
*/
public fun CharArray.asIterable(): Iterable<Char> {
if (isEmpty()) return emptyList()
return Iterable { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun <T> Array<out T>.asSequence(): Sequence<T> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun ByteArray.asSequence(): Sequence<Byte> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun ShortArray.asSequence(): Sequence<Short> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun IntArray.asSequence(): Sequence<Int> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun LongArray.asSequence(): Sequence<Long> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun FloatArray.asSequence(): Sequence<Float> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun DoubleArray.asSequence(): Sequence<Double> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun BooleanArray.asSequence(): Sequence<Boolean> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Creates a [Sequence] instance that wraps the original array returning its elements when being iterated.
*
* @sample samples.collections.Sequences.Building.sequenceFromArray
*/
public fun CharArray.asSequence(): Sequence<Char> {
if (isEmpty()) return emptySequence()
return Sequence { this.iterator() }
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun <T> Array<out T>.sumBy(selector: (T) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun ByteArray.sumBy(selector: (Byte) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun ShortArray.sumBy(selector: (Short) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun IntArray.sumBy(selector: (Int) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun LongArray.sumBy(selector: (Long) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun FloatArray.sumBy(selector: (Float) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun DoubleArray.sumBy(selector: (Double) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun BooleanArray.sumBy(selector: (Boolean) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
/**
* Returns the sum of all values produced by [selector] function applied to each element in the array.
*/
public inline fun CharArray.sumBy(selector: (Char) -> Int): Int {
var sum: Int = 0
for (element in this) {
sum += selector(element)
}
return sum
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.collections
/**
* Returns a single list of all elements from all arrays in the given array.
*/
public fun <T> Array<out Array<out T>>.flatten(): List<T> {
val result = ArrayList<T>(sumBy { it.size })
for (element in this) {
result.addAll(element)
}
return result
}
/**
* Returns a pair of lists, where
* *first* list is built from the first values of each pair from this array,
* *second* list is built from the second values of each pair from this array.
*/
public fun <T, R> Array<out Pair<T, R>>.unzip(): Pair<List<T>, List<R>> {
val listT = ArrayList<T>(size)
val listR = ArrayList<R>(size)
for (pair in this) {
listT.add(pair.first)
listR.add(pair.second)
}
return listT to listR
}
@@ -0,0 +1,236 @@
package kotlin.collections
/**
* Represents a source of elements with a [keyOf] function, which can be applied to each element to get its key.
*
* A [Grouping] structure serves as an intermediate step in group-and-fold operations:
* they group elements by their keys and then fold each group with some aggregating operation.
*
* It is created by attaching `keySelector: (T) -> K` function to a source of elements.
* To get an instance of [Grouping] use one of `groupingBy` extension functions:
* - [Iterable.groupingBy]
* - [Sequence.groupingBy]
* - [Array.groupingBy]
* - [CharSequence.groupingBy]
*
* For the list of group-and-fold operations available, see the [extension functions](#extension-functions) for `Grouping`.
*/
@SinceKotlin("1.1")
public interface Grouping<T, out K> {
/** Returns an [Iterator] over the elements of the source of this grouping. */
fun sourceIterator(): Iterator<T>
/** Extracts the key of an [element]. */
fun keyOf(element: T): K
}
/**
* Groups elements from the [Grouping] source by key and applies [operation] to the elements of each group sequentially,
* passing the previously accumulated value and the current element as arguments, and stores the results in a new map.
*
* The key for each element is provided by the [Grouping.keyOf] function.
*
* @param operation function is invoked on each element with the following parameters:
* - `key`: the key of the group this element belongs to;
* - `accumulator`: the current value of the accumulator of the group, can be `null` if it's the first `element` encountered in the group;
* - `element`: the element from the source being aggregated;
* - `first`: indicates whether it's the first `element` encountered in the group.
*
* @return a [Map] associating the key of each group with the result of aggregation of the group elements.
*/
@SinceKotlin("1.1")
public inline fun <T, K, R> Grouping<T, K>.aggregate(
operation: (key: K, accumulator: R?, element: T, first: Boolean) -> R
): Map<K, R> {
return aggregateTo(mutableMapOf<K, R>(), operation)
}
/**
* Groups elements from the [Grouping] source by key and applies [operation] to the elements of each group sequentially,
* passing the previously accumulated value and the current element as arguments,
* and stores the results in the given [destination] map.
*
* The key for each element is provided by the [Grouping.keyOf] function.
*
* @param operation a function that is invoked on each element with the following parameters:
* - `key`: the key of the group this element belongs to;
* - `accumulator`: the current value of the accumulator of the group, can be `null` if it's the first `element` encountered in the group;
* - `element`: the element from the source being aggregated;
* - `first`: indicates whether it's the first `element` encountered in the group.
*
* If the [destination] map already has a value corresponding to some key,
* then the elements being aggregated for that key are never considered as `first`.
*
* @return the [destination] map associating the key of each group with the result of aggregation of the group elements.
*/
@SinceKotlin("1.1")
public inline fun <T, K, R, M : MutableMap<in K, R>> Grouping<T, K>.aggregateTo(
destination: M,
operation: (key: K, accumulator: R?, element: T, first: Boolean) -> R
): M {
for (e in this.sourceIterator()) {
val key = keyOf(e)
val accumulator = destination[key]
destination[key] = operation(key, accumulator, e, accumulator == null && !destination.containsKey(key))
}
return destination
}
/**
* Groups elements from the [Grouping] source by key and applies [operation] to the elements of each group sequentially,
* passing the previously accumulated value and the current element as arguments, and stores the results in a new map.
* An initial value of accumulator is provided by [initialValueSelector] function.
*
* @param initialValueSelector a function that provides an initial value of accumulator for each group.
* It's invoked with parameters:
* - `key`: the key of the group;
* - `element`: the first element being encountered in that group.
*
* @param operation a function that is invoked on each element with the following parameters:
* - `key`: the key of the group this element belongs to;
* - `accumulator`: the current value of the accumulator of the group;
* - `element`: the element from the source being accumulated.
*
* @return a [Map] associating the key of each group with the result of accumulating the group elements.
*/
@SinceKotlin("1.1")
public inline fun <T, K, R> Grouping<T, K>.fold(
initialValueSelector: (key: K, element: T) -> R,
operation: (key: K, accumulator: R, element: T) -> R
): Map<K, R> =
aggregate { key, acc, e, first -> operation(key, if (first) initialValueSelector(key, e) else acc as R, e) }
/**
* Groups elements from the [Grouping] source by key and applies [operation] to the elements of each group sequentially,
* passing the previously accumulated value and the current element as arguments,
* and stores the results in the given [destination] map.
* An initial value of accumulator is provided by [initialValueSelector] function.
*
* @param initialValueSelector a function that provides an initial value of accumulator for each group.
* It's invoked with parameters:
* - `key`: the key of the group;
* - `element`: the first element being encountered in that group.
*
* If the [destination] map already has a value corresponding to some key, that value is used as an initial value of
* the accumulator for that group and the [initialValueSelector] function is not called for that group.
*
* @param operation a function that is invoked on each element with the following parameters:
* - `key`: the key of the group this element belongs to;
* - `accumulator`: the current value of the accumulator of the group;
* - `element`: the element from the source being accumulated.
*
* @return the [destination] map associating the key of each group with the result of accumulating the group elements.
*/
@SinceKotlin("1.1")
public inline fun <T, K, R, M : MutableMap<in K, R>> Grouping<T, K>.foldTo(
destination: M,
initialValueSelector: (key: K, element: T) -> R,
operation: (key: K, accumulator: R, element: T) -> R
): M =
aggregateTo(destination) { key, acc, e, first -> operation(key, if (first) initialValueSelector(key, e) else acc as R, e) }
/**
* Groups elements from the [Grouping] source by key and applies [operation] to the elements of each group sequentially,
* passing the previously accumulated value and the current element as arguments, and stores the results in a new map.
* An initial value of accumulator is the same [initialValue] for each group.
*
* @param operation a function that is invoked on each element with the following parameters:
* - `accumulator`: the current value of the accumulator of the group;
* - `element`: the element from the source being accumulated.
*
* @return a [Map] associating the key of each group with the result of accumulating the group elements.
*/
@SinceKotlin("1.1")
public inline fun <T, K, R> Grouping<T, K>.fold(
initialValue: R,
operation: (accumulator: R, element: T) -> R
): Map<K, R> =
aggregate { _, acc, e, first -> operation(if (first) initialValue else acc as R, e) }
/**
* Groups elements from the [Grouping] source by key and applies [operation] to the elements of each group sequentially,
* passing the previously accumulated value and the current element as arguments,
* and stores the results in the given [destination] map.
* An initial value of accumulator is the same [initialValue] for each group.
*
* If the [destination] map already has a value corresponding to the key of some group,
* that value is used as an initial value of the accumulator for that group.
*
* @param operation a function that is invoked on each element with the following parameters:
* - `accumulator`: the current value of the accumulator of the group;
* - `element`: the element from the source being accumulated.
*
* @return the [destination] map associating the key of each group with the result of accumulating the group elements.
*/
@SinceKotlin("1.1")
public inline fun <T, K, R, M : MutableMap<in K, R>> Grouping<T, K>.foldTo(
destination: M,
initialValue: R,
operation: (accumulator: R, element: T) -> R
): M =
aggregateTo(destination) { _, acc, e, first -> operation(if (first) initialValue else acc as R, e) }
/**
* Groups elements from the [Grouping] source by key and applies the reducing [operation] to the elements of each group
* sequentially starting from the second element of the group,
* passing the previously accumulated value and the current element as arguments,
* and stores the results in a new map.
* An initial value of accumulator is the first element of the group.
*
* @param operation a function that is invoked on each subsequent element of the group with the following parameters:
* - `key`: the key of the group this element belongs to;
* - `accumulator`: the current value of the accumulator of the group;
* - `element`: the element from the source being accumulated.
*
* @return a [Map] associating the key of each group with the result of accumulating the group elements.
*/
@SinceKotlin("1.1")
public inline fun <S, T : S, K> Grouping<T, K>.reduce(
operation: (key: K, accumulator: S, element: T) -> S
): Map<K, S> =
aggregate { key, acc, e, first ->
if (first) e else operation(key, acc as S, e)
}
/**
* Groups elements from the [Grouping] source by key and applies the reducing [operation] to the elements of each group
* sequentially starting from the second element of the group,
* passing the previously accumulated value and the current element as arguments,
* and stores the results in the given [destination] map.
* An initial value of accumulator is the first element of the group.
*
* If the [destination] map already has a value corresponding to the key of some group,
* that value is used as an initial value of the accumulator for that group and the first element of that group is also
* subjected to the [operation].
* @param operation a function that is invoked on each subsequent element of the group with the following parameters:
* - `accumulator`: the current value of the accumulator of the group;
* - `element`: the element from the source being folded;
*
* @return the [destination] map associating the key of each group with the result of accumulating the group elements.
*/
@SinceKotlin("1.1")
public inline fun <S, T : S, K, M : MutableMap<in K, S>> Grouping<T, K>.reduceTo(
destination: M,
operation: (key: K, accumulator: S, element: T) -> S
): M =
aggregateTo(destination) { key, acc, e, first ->
if (first) e else operation(key, acc as S, e)
}
/**
* Groups elements from the [Grouping] source by key and counts elements in each group to the given [destination] map.
*
* If the [destination] map already has a value corresponding to the key of some group,
* that value is used as an initial value of the counter for that group.
*
* @return the [destination] map associating the key of each group with the count of elements in the group.
*
* @sample samples.collections.Collections.Transformations.groupingByEachCount
*/
@SinceKotlin("1.1")
public fun <T, K, M : MutableMap<in K, Int>> Grouping<T, K>.eachCountTo(destination: M): M =
foldTo(destination, 0) { acc, _ -> acc + 1 }
@@ -116,6 +116,14 @@ public inline fun <K, V> Map<K, V>?.orEmpty() : Map<K, V> = this ?: emptyMap()
@kotlin.internal.InlineOnly
public inline operator fun <@kotlin.internal.OnlyInputTypes K, V> Map<out K, V>.contains(key: K) : Boolean = containsKey(key)
/**
* Allows to use the index operator for storing values in a mutable map.
*/
@kotlin.internal.InlineOnly
public inline operator fun <K, V> MutableMap<K, V>.set(key: K, value: V): Unit {
put(key, value)
}
/**
* Returns the value corresponding to the given [key], or `null` if such a key is not present in the map.
*/
@@ -20,23 +20,4 @@ public interface Sequence<out T> {
* Throws an exception if the sequence is constrained to be iterated once and `iterator` is invoked the second time.
*/
public operator fun iterator(): Iterator<T>
}
/**
* A sequence that supports drop(n) and take(n) operations
*/
internal interface DropTakeSequence<T> : Sequence<T> {
fun drop(n: Int): Sequence<T>
fun take(n: Int): Sequence<T>
}
/**
* Returns an empty sequence.
*/
public fun <T> emptySequence(): Sequence<T> = EmptySequence
private object EmptySequence : Sequence<Nothing>, DropTakeSequence<Nothing> {
override fun iterator(): Iterator<Nothing> = EmptyIterator
override fun drop(n: Int) = EmptySequence
override fun take(n: Int) = EmptySequence
}
@@ -2,6 +2,607 @@ package kotlin.sequences
import kotlin.comparisons.*
/**
* Given an [iterator] function constructs a [Sequence] that returns values through the [Iterator]
* provided by that function.
* The values are evaluated lazily, and the sequence is potentially infinite.
*
* @sample samples.collections.Sequences.Building.sequenceFromIterator
*/
@kotlin.internal.InlineOnly
public inline fun <T> Sequence(crossinline iterator: () -> Iterator<T>): Sequence<T> = object : Sequence<T> {
override fun iterator(): Iterator<T> = iterator()
}
/**
* Creates a sequence that returns all values from this enumeration. The sequence is constrained to be iterated only once.
*/
@FixmeSequences
@kotlin.internal.InlineOnly
public inline fun<T: Enum<T>> T.asSequence(): Sequence<T> = TODO() // this.values().iterator().asSequence()
/**
* Creates a sequence that returns all elements from this iterator. The sequence is constrained to be iterated only once.
*
* @sample samples.collections.Sequences.Building.sequenceFromIterator
*/
public fun <T> Iterator<T>.asSequence(): Sequence<T> = Sequence { this }.constrainOnce()
/**
* Creates a sequence that returns the specified values.
*
* @sample samples.collections.Sequences.Building.sequenceOfValues
*/
public fun <T> sequenceOf(vararg elements: T): Sequence<T> = if (elements.isEmpty()) emptySequence() else elements.asSequence()
/**
* Returns an empty sequence.
*/
public fun <T> emptySequence(): Sequence<T> = EmptySequence
private object EmptySequence : Sequence<Nothing>, DropTakeSequence<Nothing> {
override fun iterator(): Iterator<Nothing> = EmptyIterator
override fun drop(n: Int) = EmptySequence
override fun take(n: Int) = EmptySequence
}
/**
* Returns a sequence of all elements from all sequences in this sequence.
*/
public fun <T> Sequence<Sequence<T>>.flatten(): Sequence<T> = flatten { it.iterator() }
/**
* Returns a sequence of all elements from all iterables in this sequence.
*/
public fun <T> Sequence<Iterable<T>>.flatten(): Sequence<T> = flatten { it.iterator() }
private fun <T, R> Sequence<T>.flatten(iterator: (T) -> Iterator<R>): Sequence<R> {
if (this is TransformingSequence<*, *>) {
return (this as TransformingSequence<*, T>).flatten(iterator)
}
return FlatteningSequence(this, { it }, iterator)
}
/**
* Returns a pair of lists, where
* *first* list is built from the first values of each pair from this sequence,
* *second* list is built from the second values of each pair from this sequence.
*/
public fun <T, R> Sequence<Pair<T, R>>.unzip(): Pair<List<T>, List<R>> {
val listT = ArrayList<T>()
val listR = ArrayList<R>()
for (pair in this) {
listT.add(pair.first)
listR.add(pair.second)
}
return listT to listR
}
/**
* A sequence that returns the values from the underlying [sequence] that either match or do not match
* the specified [predicate].
*
* @param sendWhen If `true`, values for which the predicate returns `true` are returned. Otherwise,
* values for which the predicate returns `false` are returned
*/
internal class FilteringSequence<T>(private val sequence: Sequence<T>,
private val sendWhen: Boolean = true,
private val predicate: (T) -> Boolean
) : Sequence<T> {
override fun iterator(): Iterator<T> = object : Iterator<T> {
val iterator = sequence.iterator()
var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue
var nextItem: T? = null
private fun calcNext() {
while (iterator.hasNext()) {
val item = iterator.next()
if (predicate(item) == sendWhen) {
nextItem = item
nextState = 1
return
}
}
nextState = 0
}
override fun next(): T {
if (nextState == -1)
calcNext()
if (nextState == 0)
throw NoSuchElementException()
val result = nextItem
nextItem = null
nextState = -1
return result as T
}
override fun hasNext(): Boolean {
if (nextState == -1)
calcNext()
return nextState == 1
}
}
}
/**
* A sequence which returns the results of applying the given [transformer] function to the values
* in the underlying [sequence].
*/
internal class TransformingSequence<T, R>
constructor(private val sequence: Sequence<T>, private val transformer: (T) -> R) : Sequence<R> {
override fun iterator(): Iterator<R> = object : Iterator<R> {
val iterator = sequence.iterator()
override fun next(): R {
return transformer(iterator.next())
}
override fun hasNext(): Boolean {
return iterator.hasNext()
}
}
internal fun <E> flatten(iterator: (R) -> Iterator<E>): Sequence<E> {
return FlatteningSequence<T, R, E>(sequence, transformer, iterator)
}
}
/**
* A sequence which returns the results of applying the given [transformer] function to the values
* in the underlying [sequence], where the transformer function takes the index of the value in the underlying
* sequence along with the value itself.
*/
internal class TransformingIndexedSequence<T, R>
constructor(private val sequence: Sequence<T>, private val transformer: (Int, T) -> R) : Sequence<R> {
override fun iterator(): Iterator<R> = object : Iterator<R> {
val iterator = sequence.iterator()
var index = 0
override fun next(): R {
return transformer(index++, iterator.next())
}
override fun hasNext(): Boolean {
return iterator.hasNext()
}
}
}
/**
* A sequence which combines values from the underlying [sequence] with their indices and returns them as
* [IndexedValue] objects.
*/
internal class IndexingSequence<T>
constructor(private val sequence: Sequence<T>) : Sequence<IndexedValue<T>> {
override fun iterator(): Iterator<IndexedValue<T>> = object : Iterator<IndexedValue<T>> {
val iterator = sequence.iterator()
var index = 0
override fun next(): IndexedValue<T> {
return IndexedValue(index++, iterator.next())
}
override fun hasNext(): Boolean {
return iterator.hasNext()
}
}
}
/**
* A sequence which takes the values from two parallel underlying sequences, passes them to the given
* [transform] function and returns the values returned by that function. The sequence stops returning
* values as soon as one of the underlying sequences stops returning values.
*/
internal class MergingSequence<T1, T2, V>
constructor(private val sequence1: Sequence<T1>,
private val sequence2: Sequence<T2>,
private val transform: (T1, T2) -> V
) : Sequence<V> {
override fun iterator(): Iterator<V> = object : Iterator<V> {
val iterator1 = sequence1.iterator()
val iterator2 = sequence2.iterator()
override fun next(): V {
return transform(iterator1.next(), iterator2.next())
}
override fun hasNext(): Boolean {
return iterator1.hasNext() && iterator2.hasNext()
}
}
}
internal class FlatteningSequence<T, R, E>
constructor(
private val sequence: Sequence<T>,
private val transformer: (T) -> R,
private val iterator: (R) -> Iterator<E>
) : Sequence<E> {
override fun iterator(): Iterator<E> = object : Iterator<E> {
val iterator = sequence.iterator()
var itemIterator: Iterator<E>? = null
override fun next(): E {
if (!ensureItemIterator())
throw NoSuchElementException()
return itemIterator!!.next()
}
override fun hasNext(): Boolean {
return ensureItemIterator()
}
private fun ensureItemIterator(): Boolean {
if (itemIterator?.hasNext() == false)
itemIterator = null
while (itemIterator == null) {
if (!iterator.hasNext()) {
return false
} else {
val element = iterator.next()
val nextItemIterator = iterator(transformer(element))
if (nextItemIterator.hasNext()) {
itemIterator = nextItemIterator
return true
}
}
}
return true
}
}
}
/**
* A sequence that supports drop(n) and take(n) operations
*/
internal interface DropTakeSequence<T> : Sequence<T> {
fun drop(n: Int): Sequence<T>
fun take(n: Int): Sequence<T>
}
/**
* A sequence that skips [startIndex] values from the underlying [sequence]
* and stops returning values right before [endIndex], i.e. stops at `endIndex - 1`
*/
internal class SubSequence<T> (
private val sequence: Sequence<T>,
private val startIndex: Int,
private val endIndex: Int
): Sequence<T>, DropTakeSequence<T> {
init {
require(startIndex >= 0) { "startIndex should be non-negative, but is $startIndex" }
require(endIndex >= 0) { "endIndex should be non-negative, but is $endIndex" }
require(endIndex >= startIndex) { "endIndex should be not less than startIndex, but was $endIndex < $startIndex"}
}
private val count: Int get() = endIndex - startIndex
override fun drop(n: Int): Sequence<T> = if (n >= count) emptySequence() else SubSequence(sequence, startIndex + n, endIndex)
override fun take(n: Int): Sequence<T> = if (n >= count) this else SubSequence(sequence, startIndex, startIndex + n)
override fun iterator() = object : Iterator<T> {
val iterator = sequence.iterator()
var position = 0
// Shouldn't be called from constructor to avoid premature iteration
private fun drop() {
while(position < startIndex && iterator.hasNext()) {
iterator.next()
position++
}
}
override fun hasNext(): Boolean {
drop()
return (position < endIndex) && iterator.hasNext()
}
override fun next(): T {
drop()
if (position >= endIndex)
throw NoSuchElementException()
position++
return iterator.next()
}
}
}
/**
* A sequence that returns at most [count] values from the underlying [sequence], and stops returning values
* as soon as that count is reached.
*/
internal class TakeSequence<T> (
private val sequence: Sequence<T>,
private val count: Int
) : Sequence<T>, DropTakeSequence<T> {
init {
require (count >= 0) { "count must be non-negative, but was $count." }
}
override fun drop(n: Int): Sequence<T> = if (n >= count) emptySequence() else SubSequence(sequence, n, count)
override fun take(n: Int): Sequence<T> = if (n >= count) this else TakeSequence(sequence, n)
override fun iterator(): Iterator<T> = object : Iterator<T> {
var left = count
val iterator = sequence.iterator()
override fun next(): T {
if (left == 0)
throw NoSuchElementException()
left--
return iterator.next()
}
override fun hasNext(): Boolean {
return left > 0 && iterator.hasNext()
}
}
}
/**
* A sequence that returns values from the underlying [sequence] while the [predicate] function returns
* `true`, and stops returning values once the function returns `false` for the next element.
*/
internal class TakeWhileSequence<T>
constructor(private val sequence: Sequence<T>,
private val predicate: (T) -> Boolean
) : Sequence<T> {
override fun iterator(): Iterator<T> = object : Iterator<T> {
val iterator = sequence.iterator()
var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue
var nextItem: T? = null
private fun calcNext() {
if (iterator.hasNext()) {
val item = iterator.next()
if (predicate(item)) {
nextState = 1
nextItem = item
return
}
}
nextState = 0
}
override fun next(): T {
if (nextState == -1)
calcNext() // will change nextState
if (nextState == 0)
throw NoSuchElementException()
val result = nextItem as T
// Clean next to avoid keeping reference on yielded instance
nextItem = null
nextState = -1
return result
}
override fun hasNext(): Boolean {
if (nextState == -1)
calcNext() // will change nextState
return nextState == 1
}
}
}
/**
* A sequence that skips the specified number of values from the underlying [sequence] and returns
* all values after that.
*/
internal class DropSequence<T> (
private val sequence: Sequence<T>,
private val count: Int
) : Sequence<T>, DropTakeSequence<T> {
init {
require (count >= 0) { "count must be non-negative, but was $count." }
}
override fun drop(n: Int): Sequence<T> = DropSequence(sequence, count + n)
override fun take(n: Int): Sequence<T> = SubSequence(sequence, count, count + n)
override fun iterator(): Iterator<T> = object : Iterator<T> {
val iterator = sequence.iterator()
var left = count
// Shouldn't be called from constructor to avoid premature iteration
private fun drop() {
while (left > 0 && iterator.hasNext()) {
iterator.next()
left--
}
}
override fun next(): T {
drop()
return iterator.next()
}
override fun hasNext(): Boolean {
drop()
return iterator.hasNext()
}
}
}
/**
* A sequence that skips the values from the underlying [sequence] while the given [predicate] returns `true` and returns
* all values after that.
*/
internal class DropWhileSequence<T>
constructor(private val sequence: Sequence<T>,
private val predicate: (T) -> Boolean
) : Sequence<T> {
override fun iterator(): Iterator<T> = object : Iterator<T> {
val iterator = sequence.iterator()
var dropState: Int = -1 // -1 for not dropping, 1 for nextItem, 0 for normal iteration
var nextItem: T? = null
private fun drop() {
while (iterator.hasNext()) {
val item = iterator.next()
if (!predicate(item)) {
nextItem = item
dropState = 1
return
}
}
dropState = 0
}
override fun next(): T {
if (dropState == -1)
drop()
if (dropState == 1) {
val result = nextItem as T
nextItem = null
dropState = 0
return result
}
return iterator.next()
}
override fun hasNext(): Boolean {
if (dropState == -1)
drop()
return dropState == 1 || iterator.hasNext()
}
}
}
internal class DistinctSequence<T, K>(private val source : Sequence<T>, private val keySelector : (T) -> K) : Sequence<T> {
override fun iterator(): Iterator<T> = DistinctIterator(source.iterator(), keySelector)
}
private class DistinctIterator<T, K>(private val source : Iterator<T>, private val keySelector : (T) -> K) : AbstractIterator<T>() {
private val observed = HashSet<K>()
override fun computeNext() {
while (source.hasNext()) {
val next = source.next()
val key = keySelector(next)
if (observed.add(key)) {
setNext(next)
return
}
}
done()
}
}
private class GeneratorSequence<T: Any>(private val getInitialValue: () -> T?, private val getNextValue: (T) -> T?): Sequence<T> {
override fun iterator(): Iterator<T> = object : Iterator<T> {
var nextItem: T? = null
var nextState: Int = -2 // -2 for initial unknown, -1 for next unknown, 0 for done, 1 for continue
private fun calcNext() {
nextItem = if (nextState == -2) getInitialValue() else getNextValue(nextItem!!)
nextState = if (nextItem == null) 0 else 1
}
override fun next(): T {
if (nextState < 0)
calcNext()
if (nextState == 0)
throw NoSuchElementException()
val result = nextItem as T
// Do not clean nextItem (to avoid keeping reference on yielded instance) -- need to keep state for getNextValue
nextState = -1
return result
}
override fun hasNext(): Boolean {
if (nextState < 0)
calcNext()
return nextState == 1
}
}
}
/**
* 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)
}
@FixmeConcurrency
private class ConstrainedOnceSequence<T>(sequence: Sequence<T>) : Sequence<T> {
// TODO: not MT friendly.
private var sequenceRef : Sequence<T>? = sequence
override fun iterator(): Iterator<T> {
val sequence = sequenceRef
if (sequence == null) throw IllegalStateException("This sequence can be consumed only once.")
sequenceRef = null
return sequence.iterator()
}
}
/**
* Returns a sequence which invokes the function to calculate the next value on each iteration until the function returns `null`.
*
* The returned sequence is constrained to be iterated only once.
*
* @see constrainOnce
* @see kotlin.coroutines.experimental.buildSequence
*
* @sample samples.collections.Sequences.Building.generateSequence
*/
public fun <T : Any> generateSequence(nextFunction: () -> T?): Sequence<T> {
return GeneratorSequence(nextFunction, { nextFunction() }).constrainOnce()
}
/**
* Returns a sequence defined by the starting value [seed] and the function [nextFunction],
* which is invoked to calculate the next value based on the previous one on each iteration.
*
* The sequence produces values until it encounters first `null` value.
* If [seed] is `null`, an empty sequence is produced.
*
* The sequence can be iterated multiple times, each time starting with [seed].
*
* @see kotlin.coroutines.experimental.buildSequence
*
* @sample samples.collections.Sequences.Building.generateSequenceWithSeed
*/
@kotlin.internal.LowPriorityInOverloadResolution
public fun <T : Any> generateSequence(seed: T?, nextFunction: (T) -> T?): Sequence<T> =
if (seed == null)
EmptySequence
else
GeneratorSequence({ seed }, nextFunction)
/**
* Returns a sequence defined by the function [seedFunction], which is invoked to produce the starting value,
* and the [nextFunction], which is invoked to calculate the next value based on the previous one on each iteration.
*
* The sequence produces values until it encounters first `null` value.
* If [seedFunction] returns `null`, an empty sequence is produced.
*
* The sequence can be iterated multiple times.
*
* @see kotlin.coroutines.experimental.buildSequence
*
* @sample samples.collections.Sequences.Building.generateSequenceWithLazySeed
*/
public fun <T: Any> generateSequence(seedFunction: () -> T?, nextFunction: (T) -> T?): Sequence<T> =
GeneratorSequence(seedFunction, nextFunction)
/**
* Returns `true` if [element] is found in the sequence.
*/
@@ -276,72 +877,20 @@ public inline fun <T> Sequence<T>.singleOrNull(predicate: (T) -> Boolean): T? {
/**
* Returns a sequence containing all elements except first [n] elements.
*/
@FixmeSequences
public fun <T> Sequence<T>.drop(n: Int): Sequence<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
TODO()
//return when {
// n == 0 -> this
// this is DropTakeSequence -> this.drop(n)
// else -> DropSequence(this, n)
//}
return when {
n == 0 -> this
this is DropTakeSequence -> this.drop(n)
else -> DropSequence(this, n)
}
}
/**
* Returns a sequence containing all elements except first elements that satisfy the given [predicate].
*/
@FixmeSequences
public fun <T> Sequence<T>.dropWhile(predicate: (T) -> Boolean): Sequence<T> {
TODO()
//return DropWhileSequence(this, predicate)
}
/**
* A sequence that returns the values from the underlying [sequence] that either match or do not match
* the specified [predicate].
*
* @param sendWhen If `true`, values for which the predicate returns `true` are returned. Otherwise,
* values for which the predicate returns `false` are returned
*/
internal class FilteringSequence<T>(private val sequence: Sequence<T>,
private val sendWhen: Boolean = true,
private val predicate: (T) -> Boolean
) : Sequence<T> {
override fun iterator(): Iterator<T> = object : Iterator<T> {
val iterator = sequence.iterator()
var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue
var nextItem: T? = null
private fun calcNext() {
while (iterator.hasNext()) {
val item = iterator.next()
if (predicate(item) == sendWhen) {
nextItem = item
nextState = 1
return
}
}
nextState = 0
}
override fun next(): T {
if (nextState == -1)
calcNext()
if (nextState == 0)
throw NoSuchElementException()
val result = nextItem
nextItem = null
nextState = -1
return result as T
}
override fun hasNext(): Boolean {
if (nextState == -1)
calcNext()
return nextState == 1
}
}
return DropWhileSequence(this, predicate)
}
/**
@@ -356,11 +905,9 @@ public fun <T> Sequence<T>.filter(predicate: (T) -> Boolean): Sequence<T> {
* @param [predicate] function that takes the index of an element and the element itself
* and returns the result of predicate evaluation on the element.
*/
@Fixme
public fun <T> Sequence<T>.filterIndexed(predicate: (Int, T) -> Boolean): Sequence<T> {
TODO()
// TODO: Rewrite with generalized MapFilterIndexingSequence
// return TransformingSequence(FilteringSequence(IndexingSequence(this), true, { predicate(it.index, it.value) }), { it.value })
return TransformingSequence(FilteringSequence(IndexingSequence(this), true, { predicate(it.index, it.value) }), { it.value })
}
/**
@@ -437,24 +984,20 @@ public inline fun <T, C : MutableCollection<in T>> Sequence<T>.filterTo(destinat
/**
* Returns a sequence containing first [n] elements.
*/
@FixmeSequences
public fun <T> Sequence<T>.take(n: Int): Sequence<T> {
//require(n >= 0) { "Requested element count $n is less than zero." }
//return when {
// n == 0 -> emptySequence()
// this is DropTakeSequence -> this.take(n)
// else -> TakeSequence(this, n)
//}
TODO()
require(n >= 0) { "Requested element count $n is less than zero." }
return when {
n == 0 -> emptySequence()
this is DropTakeSequence -> this.take(n)
else -> TakeSequence(this, n)
}
}
/**
* Returns a sequence containing first elements satisfying the given [predicate].
*/
@FixmeSequences
public fun <T> Sequence<T>.takeWhile(predicate: (T) -> Boolean): Sequence<T> {
// return TakeWhileSequence(this, predicate)
TODO()
return TakeWhileSequence(this, predicate)
}
/**
@@ -623,10 +1166,8 @@ public fun <T> Sequence<T>.toSet(): Set<T> {
/**
* Returns a single sequence of all elements from results of [transform] function being invoked on each element of original sequence.
*/
@FixmeSequences
public fun <T, R> Sequence<T>.flatMap(transform: (T) -> Sequence<R>): Sequence<R> {
// return FlatteningSequence(this, transform, { it.iterator() })
TODO()
return FlatteningSequence(this, transform, { it.iterator() })
}
/**
@@ -704,23 +1245,19 @@ public inline fun <T, K, V, M : MutableMap<in K, MutableList<V>>> Sequence<T>.gr
* Creates a [Grouping] source from a sequence to be used later with one of group-and-fold operations
* using the specified [keySelector] function to extract a key from each element.
*/
// @Fixme
// public inline fun <T, K> Sequence<T>.groupingBy(crossinline keySelector: (T) -> K): Grouping<T, K> {
// TODO()
//return object : Grouping<T, K> {
// override fun sourceIterator(): Iterator<T> = this@groupingBy.iterator()
// override fun keyOf(element: T): K = keySelector(element)
//}
//}
public inline fun <T, K> Sequence<T>.groupingBy(crossinline keySelector: (T) -> K): Grouping<T, K> {
return object : Grouping<T, K> {
override fun sourceIterator(): Iterator<T> = this@groupingBy.iterator()
override fun keyOf(element: T): K = keySelector(element)
}
}
/**
* Returns a sequence containing the results of applying the given [transform] function
* to each element in the original sequence.
*/
@FixmeSequences
public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R> {
// return TransformingSequence(this, transform)
TODO()
return TransformingSequence(this, transform)
}
/**
@@ -729,10 +1266,8 @@ public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R> {
* @param [transform] function that takes the index of an element and the element itself
* and returns the result of the transform applied to the element.
*/
@FixmeSequences
public fun <T, R> Sequence<T>.mapIndexed(transform: (Int, T) -> R): Sequence<R> {
TODO()
//return TransformingIndexedSequence(this, transform)
return TransformingIndexedSequence(this, transform)
}
/**
@@ -741,10 +1276,8 @@ public fun <T, R> Sequence<T>.mapIndexed(transform: (Int, T) -> R): Sequence<R>
* @param [transform] function that takes the index of an element and the element itself
* and returns the result of the transform applied to the element.
*/
@FixmeSequences
public fun <T, R : Any> Sequence<T>.mapIndexedNotNull(transform: (Int, T) -> R?): Sequence<R> {
TODO()
//return TransformingIndexedSequence(this, transform).filterNotNull()
return TransformingIndexedSequence(this, transform).filterNotNull()
}
/**
@@ -775,10 +1308,8 @@ public inline fun <T, R, C : MutableCollection<in R>> Sequence<T>.mapIndexedTo(d
* Returns a sequence containing only the non-null results of applying the given [transform] function
* to each element in the original sequence.
*/
@FixmeSequences
public fun <T, R : Any> Sequence<T>.mapNotNull(transform: (T) -> R?): Sequence<R> {
// return TransformingSequence(this, transform).filterNotNull()
TODO()
return TransformingSequence(this, transform).filterNotNull()
}
/**
@@ -803,10 +1334,8 @@ public inline fun <T, R, C : MutableCollection<in R>> Sequence<T>.mapTo(destinat
/**
* Returns a sequence of [IndexedValue] for each element of the original sequence.
*/
@Fixme
public fun <T> Sequence<T>.withIndex(): Sequence<IndexedValue<T>> {
// return IndexingSequence(this)
TODO()
return IndexingSequence(this)
}
/**
@@ -824,10 +1353,8 @@ public fun <T> Sequence<T>.distinct(): Sequence<T> {
*
* The elements in the resulting sequence are in the same order as they were in the source sequence.
*/
@FixmeSequences
public fun <T, K> Sequence<T>.distinctBy(selector: (T) -> K): Sequence<T> {
// return DistinctSequence(this, selector)
TODO()
return DistinctSequence(this, selector)
}
/**
@@ -1266,10 +1793,8 @@ public inline fun <T> Sequence<T>.partition(predicate: (T) -> Boolean): Pair<Lis
/**
* Returns a sequence containing all elements of the original sequence and then the given [element].
*/
@FixmeSequences
public operator fun <T> Sequence<T>.plus(element: T): Sequence<T> {
TODO()
// return sequenceOf(this, sequenceOf(element)).flatten()
return sequenceOf(this, sequenceOf(element)).flatten()
}
/**
@@ -1288,10 +1813,8 @@ public operator fun <T> Sequence<T>.plus(elements: Array<out T>): Sequence<T> {
* Note that the source sequence and the collection being added are iterated only when an `iterator` is requested from
* the resulting sequence. Changing any of them between successive calls to `iterator` may affect the result.
*/
@FixmeSequences
public operator fun <T> Sequence<T>.plus(elements: Iterable<T>): Sequence<T> {
// return sequenceOf(this, elements.asSequence()).flatten()
TODO()
return sequenceOf(this, elements.asSequence()).flatten()
}
/**
@@ -1300,10 +1823,8 @@ public operator fun <T> Sequence<T>.plus(elements: Iterable<T>): Sequence<T> {
* Note that the source sequence and the sequence being added are iterated only when an `iterator` is requested from
* the resulting sequence. Changing any of them between successive calls to `iterator` may affect the result.
*/
@FixmeSequences
public operator fun <T> Sequence<T>.plus(elements: Sequence<T>): Sequence<T> {
//return sequenceOf(this, elements).flatten()
TODO()
return sequenceOf(this, elements).flatten()
}
/**
@@ -1318,19 +1839,15 @@ public inline fun <T> Sequence<T>.plusElement(element: T): Sequence<T> {
* Returns a sequence of pairs built from elements of both sequences with same indexes.
* Resulting sequence has length of shortest input sequence.
*/
@FixmeSequences
public infix fun <T, R> Sequence<T>.zip(other: Sequence<R>): Sequence<Pair<T, R>> {
TODO()
//return MergingSequence(this, other) { t1, t2 -> t1 to t2 }
return MergingSequence(this, other) { t1, t2 -> t1 to t2 }
}
/**
* Returns a sequence of values built from elements of both collections with same indexes using provided [transform]. Resulting sequence has length of shortest input sequences.
*/
@FixmeSequences
public fun <T, R, V> Sequence<T>.zip(other: Sequence<R>, transform: (T, R) -> V): Sequence<V> {
// return MergingSequence(this, other, transform)
TODO()
return MergingSequence(this, other, transform)
}
/**
@@ -1339,10 +1856,7 @@ public fun <T, R, V> Sequence<T>.zip(other: Sequence<R>, transform: (T, R) -> V)
* If the collection could be huge, you can specify a non-negative value of [limit], in which case only the first [limit]
* elements will be appended, followed by the [truncated] string (which defaults to "...").
*/
@FixmeSequences
public fun <T, A : Appendable> Sequence<T>.joinTo(buffer: A, separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): A {
TODO()
/*
buffer.append(prefix)
var count = 0
for (element in this) {
@@ -1353,7 +1867,7 @@ public fun <T, A : Appendable> Sequence<T>.joinTo(buffer: A, separator: CharSequ
}
if (limit >= 0 && count > limit) buffer.append(truncated)
buffer.append(postfix)
return buffer */
return buffer
}
/**