Reverting the pull-request #29 due to the upcoming redesign of the lazy collections API
This commit is contained in:
@@ -1,105 +0,0 @@
|
|||||||
package kotlin.sequences
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A sequence of items of type *T* that are lazily produced by the given closure
|
|
||||||
*
|
|
||||||
* @param next a closure yielding the next element in this sequence
|
|
||||||
*
|
|
||||||
* @author [Franck Rasolo](http://www.linkedin.com/in/franckrasolo)
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
class Sequence<in T>(val next: () -> T?) : Iterable<T> {
|
|
||||||
|
|
||||||
override fun iterator(): Iterator<T> = YieldingIterator { (next)() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retains only elements that satisfy the given predicate
|
|
||||||
*
|
|
||||||
* @param predicate the predicate evaluated against objects of type *T*
|
|
||||||
*/
|
|
||||||
fun filter(predicate: (T) -> Boolean): Sequence<T> {
|
|
||||||
val iterator = iterator()
|
|
||||||
|
|
||||||
fun next(): T? {
|
|
||||||
while (iterator.hasNext) {
|
|
||||||
val item = iterator.next()
|
|
||||||
if ((predicate)(item)) return item
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return Sequence<T> { next() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reduces this sequence using the binary operator, from left to right.
|
|
||||||
* Only finite sequences can be reduced.
|
|
||||||
*
|
|
||||||
* @param seed an initial value, typically the left-identity of the operator
|
|
||||||
* @param operator a binary operator
|
|
||||||
*/
|
|
||||||
fun fold(seed: T, operator: (T, T) -> T): T {
|
|
||||||
var result = seed
|
|
||||||
for (item in this) result = (operator)(result, item)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces a sequence obtained by applying *transform* to each element of this sequence
|
|
||||||
*
|
|
||||||
* @param transform the function transforming an object of type *T* into an object of type *R*
|
|
||||||
*/
|
|
||||||
fun <in R> map(transform: (T) -> R): Sequence<R> {
|
|
||||||
val iterator = iterator()
|
|
||||||
fun next(): R? = if (iterator.hasNext) (transform)(iterator.next()) else null
|
|
||||||
return Sequence<R> { next() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts the prefix of this sequence as a finite sequence of length *n*
|
|
||||||
*
|
|
||||||
* @param n the number of elements of the extracted sequence
|
|
||||||
*/
|
|
||||||
fun take(n: Int): Sequence<T> {
|
|
||||||
fun countTo(n: Int): (T) -> Boolean {
|
|
||||||
var count = 0
|
|
||||||
return { ++count; count <= n }
|
|
||||||
}
|
|
||||||
return takeWhile(countTo(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts the longest prefix, possibly empty, of this sequence where each element satisfies *predicate*
|
|
||||||
*
|
|
||||||
* @param predicate the predicate evaluated against objects of type *T*
|
|
||||||
*/
|
|
||||||
fun takeWhile(predicate: (T) -> Boolean): Sequence<T> {
|
|
||||||
val iterator = iterator()
|
|
||||||
|
|
||||||
fun next(): T? {
|
|
||||||
if (iterator.hasNext) {
|
|
||||||
val item = iterator.next()
|
|
||||||
if ((predicate)(item)) return item
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return Sequence<T> { next() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class YieldingIterator<T>(val yield: () -> T?) : Iterator<T> {
|
|
||||||
var current : T? = (yield)()
|
|
||||||
|
|
||||||
override val hasNext: Boolean
|
|
||||||
get() = current != null
|
|
||||||
|
|
||||||
override fun next(): T {
|
|
||||||
val next = current
|
|
||||||
if (next != null) {
|
|
||||||
current = (yield)()
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
throw java.util.NoSuchElementException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
package kotlin.sequences
|
|
||||||
|
|
||||||
/** Creates a sequence from the given list of *elements* */
|
|
||||||
inline fun <T> sequence(vararg elements: T): Sequence<T> = asSequence(elements.iterator())
|
|
||||||
|
|
||||||
/** Creates a sequence of elements lazily obtained from this Kotlin [[jet.Iterable]] */
|
|
||||||
inline fun <T> Iterable<T>.asSequence(): Sequence<T> = asSequence(iterator())
|
|
||||||
|
|
||||||
/** Creates a sequence of elements lazily obtained from this Java [[java.lang.Iterable]] */
|
|
||||||
inline fun <T> java.lang.Iterable<T>.asSequence(): Sequence<T> = asSequence(iterator().sure() as java.util.Iterator<T>)
|
|
||||||
|
|
||||||
private fun <T> asSequence(iterator: Iterator<T>) = Sequence<T> { if (iterator.hasNext) iterator.next() else null }
|
|
||||||
private fun <T> asSequence(iterator: java.util.Iterator<T>) = Sequence<T> { if (iterator.hasNext()) iterator.next() else null }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces the [cartesian product](http://en.wikipedia.org/wiki/Cartesian_product#n-ary_product) as a sequence of ordered pairs of elements lazily obtained
|
|
||||||
* from two [[Iterable]] instances
|
|
||||||
*/
|
|
||||||
fun <T> Iterable<T>.times(other: Iterable<T>): Sequence<#(T, T)> {
|
|
||||||
val first = iterator(); var second = other.iterator(); var a: T? = null
|
|
||||||
|
|
||||||
fun nextPair(): #(T, T)? {
|
|
||||||
if (a == null && first.hasNext) a = first.next()
|
|
||||||
if (second.hasNext) return #(a.sure(), second.next())
|
|
||||||
if (first.hasNext) {
|
|
||||||
a = first.next(); second = other.iterator()
|
|
||||||
return #(a.sure(), second.next())
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return Sequence<#(T, T)> { nextPair() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Partitions this string into groups of fixed size strings
|
|
||||||
*
|
|
||||||
* @param size the number of characters per group
|
|
||||||
*
|
|
||||||
* @return a sequence of strings of size *size*, except the last will be truncated if the elements do not divide evenly
|
|
||||||
*/
|
|
||||||
fun String.grouped(size: Int, iterator: CharIterator = iterator()): Sequence<String> {
|
|
||||||
fun nextGroup(): String? {
|
|
||||||
if (iterator.hasNext) {
|
|
||||||
val window = StringBuilder()
|
|
||||||
for (i in 1..size) if (iterator.hasNext) window.append(iterator.next())
|
|
||||||
return window.toString()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return Sequence<String> { nextGroup() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Groups elements in fixed size blocks by passing a *sliding window* over them, as opposed to partitioning them, as is done in `String.grouped`
|
|
||||||
*
|
|
||||||
* @param size the number of characters per group
|
|
||||||
*
|
|
||||||
* @return a sequence of strings of size *size*, except the last and the only element will be truncated if there are fewer characters than *size*
|
|
||||||
*/
|
|
||||||
fun String.sliding(size: Int, iterator: CharIterator = iterator()): Sequence<String> {
|
|
||||||
val window = StringBuilder()
|
|
||||||
|
|
||||||
fun nextWindow(): String? {
|
|
||||||
if (window.length() == 0) {
|
|
||||||
for (i in 1..size) if (iterator.hasNext) window.append(iterator.next())
|
|
||||||
return window.toString()
|
|
||||||
}
|
|
||||||
return if (iterator.hasNext) window.deleteCharAt(0)?.append(iterator.next()).toString() else null
|
|
||||||
}
|
|
||||||
|
|
||||||
return Sequence<String> { nextWindow() }
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package test.sequences
|
|
||||||
|
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
import kotlin.sequences.asSequence
|
|
||||||
import kotlin.sequences.sequence
|
|
||||||
import kotlin.util.arrayList
|
|
||||||
import kotlin.util.fold
|
|
||||||
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class SequenceTest {
|
|
||||||
|
|
||||||
Test fun filterReturnsTheExpectedSequence() {
|
|
||||||
val actual = (1..10).asSequence().filter { it % 2 == 0 }
|
|
||||||
assertEquals(arrayList(2, 4, 6, 8, 10), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun filterReturnsAnEmptySequenceWhenNoElementMatchesThePredicate() {
|
|
||||||
val actual = sequence(1, 3, 4, 7).filter { it > 7 }
|
|
||||||
assertEquals(ArrayList<Int>(), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun foldReturnsTheExpectedReduction() {
|
|
||||||
val actual = (1..10).asSequence().filter { it % 2 == 1 }.fold(0, sum)
|
|
||||||
assertEquals(1 + 3 + 5 + 7 + 9, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun foldReturnsTheInitialValueForAnEmptySequence() {
|
|
||||||
val initialValue = 0
|
|
||||||
val actual = (1..0).asSequence().fold(initialValue, sum)
|
|
||||||
|
|
||||||
assertEquals(initialValue, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sum = { (a : Int, b : Int) -> a + b }
|
|
||||||
|
|
||||||
Test fun mapReturnsTheExpectedTransformation() {
|
|
||||||
val actual = sequence(1, 3, 4, 7).map { it * 2 }
|
|
||||||
assertEquals(arrayList(2, 6, 8, 14), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun takeReturnsTheFirstNElements() {
|
|
||||||
var actual = (1..5).asSequence().take(3)
|
|
||||||
assertEquals(arrayList(1, 2, 3), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun takeReturnsTheEntireSequenceWhenTheNumberOfElementsIsGreaterThanItsSize() {
|
|
||||||
val actual = sequence(1, 2).take(3)
|
|
||||||
assertEquals(arrayList(1, 2), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun takeReturnsAnEmptySequenceForAnEmptySequence() {
|
|
||||||
val actual = (1..0).asSequence().take(3)
|
|
||||||
assertEquals(ArrayList<Int>(), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun takeReturnsAnEmptySequenceWhenTheNumberOfElementsToExtractIsZero() {
|
|
||||||
val actual = sequence(1, 2).take(0)
|
|
||||||
assertEquals(ArrayList<Int>(), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun takeReturnsAnEmptySequenceWhenTheNumberOfElementsToExtractIsNegative() {
|
|
||||||
val actual = sequence(1, 2).take(-1)
|
|
||||||
assertEquals(ArrayList<Int>(), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun takeWhileReturnsTheFirstElementsMatchingAGivenPredicate() {
|
|
||||||
val actual = sequence(1, 2, 3, 4, 1, 2, 3, 4).takeWhile { it < 3 }
|
|
||||||
assertEquals(arrayList(1, 2), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun takeWhileReturnsTheEntireSequenceWhenThePredicateMatchesAllElements() {
|
|
||||||
val actual = sequence(1, 2, 3).takeWhile { it < 9 }
|
|
||||||
assertEquals(arrayList(1, 2, 3), actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun takeWhileReturnsAnEmptySequenceWhenThePredicateMatchesNothing() {
|
|
||||||
val actual = sequence(1, 2, 3).takeWhile { it < 0 }
|
|
||||||
assertEquals(ArrayList<Int>(), actual.toList())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package test.sequences
|
|
||||||
|
|
||||||
import kotlin.sequences.grouped
|
|
||||||
import kotlin.sequences.sequence
|
|
||||||
import kotlin.sequences.sliding
|
|
||||||
import kotlin.sequences.times
|
|
||||||
import kotlin.util.arrayList
|
|
||||||
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class SequencesTest {
|
|
||||||
|
|
||||||
Test fun timesComputesTheCartesianSquareOfAnIterable() {
|
|
||||||
val actual = (1..3) * (1..3)
|
|
||||||
val expected = arrayList(
|
|
||||||
#(1, 1), #(1, 2), #(1, 3),
|
|
||||||
#(2, 1), #(2, 2), #(2, 3),
|
|
||||||
#(3, 1), #(3, 2), #(3, 3)
|
|
||||||
)
|
|
||||||
assertEquals(expected, actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun timesComputesTheCartesianProductOfTwoRanges() {
|
|
||||||
val actual = (1..3) * (4..7)
|
|
||||||
val expected = arrayList(
|
|
||||||
#(1, 4), #(1, 5), #(1, 6), #(1, 7),
|
|
||||||
#(2, 4), #(2, 5), #(2, 6), #(2, 7),
|
|
||||||
#(3, 4), #(3, 5), #(3, 6), #(3, 7)
|
|
||||||
)
|
|
||||||
assertEquals(expected, actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun timesComputesTheCartesianProductOfTwoIterables() {
|
|
||||||
val actual = sequence(1, 3, 5, 7) * (4..6)
|
|
||||||
val expected = arrayList(
|
|
||||||
#(1, 4), #(1, 5), #(1, 6),
|
|
||||||
#(3, 4), #(3, 5), #(3, 6),
|
|
||||||
#(5, 4), #(5, 5), #(5, 6),
|
|
||||||
#(7, 4), #(7, 5), #(7, 6)
|
|
||||||
)
|
|
||||||
assertEquals(expected, actual.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun groupedReturnsFixedSizeGroupsOfStrings() {
|
|
||||||
val digits = """
|
|
||||||
62229893423380308135336276614282806444486645238749
|
|
||||||
30358907296290491560440772390713810515859307960866
|
|
||||||
70172427121883998797908792274921901699720888093776
|
|
||||||
65727333001053367881220235421809751254540594752243
|
|
||||||
""".trim().replaceAll("\\n", "")
|
|
||||||
|
|
||||||
val actual = digits.grouped(50).toList().get(2)
|
|
||||||
assertEquals("70172427121883998797908792274921901699720888093776", actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
Test fun slidingOverALongStringOfDigits() {
|
|
||||||
val digits = """
|
|
||||||
62229893423380308135336276614282806444486645238749
|
|
||||||
30358907296290491560440772390713810515859307960866
|
|
||||||
70172427121883998797908792274921901699720888093776
|
|
||||||
65727333001053367881220235421809751254540594752243
|
|
||||||
""".trim().replaceAll("\\n", "")
|
|
||||||
|
|
||||||
val actual = digits.sliding(3).filter { Integer.parseInt(it) > 990 }
|
|
||||||
assertEquals(arrayList("998", "997"), actual.toList())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user