Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #32 from franckrasolo/master

#KT-1653 Fixed
  • Loading branch information...
commit 9b434490c5408d1b2b4061e3e828412d5b0c707b 2 parents 77652a9 + ca4f552
@jstrachan jstrachan authored
View
151 libraries/stdlib/src/kotlin/Iterators.kt
@@ -3,36 +3,57 @@ package kotlin
import kotlin.support.AbstractIterator
import kotlin.support.FunctionIterator
-import java.util.*
-import java.util.Iterator
+/**
+ * Returns an iterator which invokes the function to calculate the next value on each iteration until the function returns *null*
+ *
+ * @includeFunction ../../test/iterators/IteratorsTest.kt fibonacci
+ */
+inline fun <T> iterate(nextFunction: () -> T?) : java.util.Iterator<T> = FunctionIterator(nextFunction)
-/** Filters the iterator for all elements of a certain sub class */
-inline fun <T, R: T> java.util.Iterator<T>.filterIsInstance(klass: Class<R>): Iterator<R> = FilterIsIterator<T,R>(this, klass)
+/** Returns an iterator over elements that are instances of a given type *R* which is a subclass of *T* */
+inline fun <T, R: T> java.util.Iterator<T>.filterIsInstance(klass: Class<R>): java.util.Iterator<R> = FilterIsIterator<T,R>(this, klass)
-private class FilterIsIterator<T, R :T>(val iter: java.util.Iterator<T>, val klass: Class<R>) : AbstractIterator<R>() {
+private class FilterIsIterator<T, R :T>(val iterator : java.util.Iterator<T>, val klass: Class<R>) : AbstractIterator<R>() {
override protected fun computeNext(): R? {
- while (iter.hasNext()) {
- val next = iter.next()
- if (klass.isInstance(next)) {
- return next as R
- }
+ while (iterator.hasNext()) {
+ val next = iterator.next()
+ if (klass.isInstance(next)) return next as R
}
done()
return null
}
}
-/** Returns a new lazy iterator containing all elements in this iteration which match the given predicate */
-inline fun <T> java.util.Iterator<T>.filter(predicate: (T)-> Boolean) : java.util.Iterator<T> {
- return FilterIterator(this, predicate)
+/**
+ * Returns an iterator over elements which match the given *predicate*
+ *
+ * @includeFunction ../../test/iterators/IteratorsTest.kt filterAndTakeWhileExtractTheElementsWithinRange
+ */
+inline fun <T> java.util.Iterator<T>.filter(predicate: (T) -> Boolean) : java.util.Iterator<T> = FilterIterator<T>(this, predicate)
+
+private class FilterIterator<T>(val iterator : java.util.Iterator<T>, val predicate: (T)-> Boolean) : AbstractIterator<T>() {
+ override protected fun computeNext(): T? {
+ while (iterator.hasNext()) {
+ val next = iterator.next()
+ if ((predicate)(next)) return next
+ }
+ done()
+ return null
+ }
}
-private class FilterIterator<T>(val iter: java.util.Iterator<T>, val predicate: (T)-> Boolean) : AbstractIterator<T>() {
+/** Returns an iterator over elements which do not match the given *predicate* */
+inline fun <T> java.util.Iterator<T>.filterNot(predicate: (T) -> Boolean) : java.util.Iterator<T> = filter { !predicate(it) }
+
+/** Returns an iterator over non-*null* elements */
+inline fun <T> java.util.Iterator<T?>?.filterNotNull() : java.util.Iterator<T> = FilterNotNullIterator(this)
+
+private class FilterNotNullIterator<T>(val iterator : java.util.Iterator<T?>?) : AbstractIterator<T>() {
override protected fun computeNext(): T? {
- while (iter.hasNext()) {
- val element = iter.next()
- if ((predicate)(element)) {
- return element
+ if (iterator != null) {
+ while (iterator.hasNext()) {
+ val next = iterator.next()
+ if (next != null) return next
}
}
done()
@@ -40,46 +61,88 @@ private class FilterIterator<T>(val iter: java.util.Iterator<T>, val predicate:
}
}
-/** Returns a new lazy iterator containing all elements in this iteration which do not match the given predicate */
-inline fun <T> java.util.Iterator<T>.filterNot(predicate: (T)-> Boolean) : java.util.Iterator<T> {
- return filter {
- !predicate(it)
- }
+/**
+ * Returns an iterator obtained by applying *transform*, a function transforming an object of type *T* into an object of type *R*
+ *
+ * @includeFunction ../../test/iterators/IteratorsTest.kt mapAndTakeWhileExtractTheTransformedElements
+ */
+inline fun <T, R> java.util.Iterator<T>.map(transform: (T) -> R): java.util.Iterator<R> = MapIterator<T, R>(this, transform)
+
+private class MapIterator<T, R>(val iterator : java.util.Iterator<T>, val transform: (T) -> R) : AbstractIterator<R>() {
+ override protected fun computeNext() : R? = if (iterator.hasNext()) (transform)(iterator.next()) else { done(); null }
}
-/** Returns a new lazy iterator containing all the non null elements in this iteration */
-inline fun <T> java.util.Iterator<T?>?.filterNotNull() : java.util.Iterator<T> = FilterNotNullIterator(this)
+/**
+ * Returns an iterator over the concatenated results of transforming each element to one or more values
+ *
+ * @includeFunction ../../test/iterators/IteratorsTest.kt flatMapAndTakeExtractTheTransformedElements
+ */
+inline fun <T, R> java.util.Iterator<T>.flatMap(transform: (T) -> java.util.Iterator<R>): java.util.Iterator<R> = FlatMapIterator<T, R>(this, transform)
-private class FilterNotNullIterator<T>(val iter: java.util.Iterator<T?>?) : AbstractIterator<T>() {
- override fun computeNext(): T? {
- if (iter != null) {
- while (iter.hasNext()) {
- val next = iter.next()
- if (next != null) {
- return next
- }
- }
+private class FlatMapIterator<T, R>(val iterator : java.util.Iterator<T>, val transform: (T) -> java.util.Iterator<R>) : AbstractIterator<R>() {
+ var transformed: java.util.Iterator<R> = iterate<R> { null }
+
+ override protected fun computeNext() : R? {
+ if (transformed.hasNext()) return transformed.next()
+ if (iterator.hasNext()) {
+ transformed = (transform)(iterator.next())
+ return computeNext()
}
done()
return null
}
}
+/**
+ * Returns an iterator restricted to the first *n* elements
+ *
+ * @includeFunction ../../test/iterators/IteratorsTest.kt takeExtractsTheFirstNElements
+ */
+inline fun <T> java.util.Iterator<T>.take(n: Int): java.util.Iterator<T> {
+ fun countTo(n: Int): (T) -> Boolean {
+ var count = 0
+ return { ++count; count <= n }
+ }
+ return takeWhile(countTo(n))
+}
/**
- * Returns a lazy iterator containing the result of transforming each item in the iteration to a one or more values which
- * are concatenated together into a single collection
- */
-/*
+ * Returns an iterator restricted to the first elements that match the given *predicate*
+ *
+ * @includeFunction ../../test/iterators/IteratorsTest.kt filterAndTakeWhileExtractTheElementsWithinRange
+ */
+inline fun <T> java.util.Iterator<T>.takeWhile(predicate: (T) -> Boolean): java.util.Iterator<T> = TakeWhileIterator<T>(this, predicate)
- TODO implement a lazy flatMap
+private class TakeWhileIterator<T>(val iterator: java.util.Iterator<T>, val predicate: (T) -> Boolean) : AbstractIterator<T>() {
+ override protected fun computeNext() : T? {
+ if (iterator.hasNext()) {
+ val item = iterator.next()
+ if ((predicate)(item)) return item
+ }
+ done()
+ return null
+ }
+}
-inline fun <T, R> java.util.Iterator<T>.flatMap(transform: (T)-> java.util.Iterator<R>) : Collection<R> {
- return flatMapTo<>(ArrayList<R>(), transform)
+/**
+ * Creates a string from the first *n (= limit)* elements separated using the *separator* and using the given *prefix* and *postfix* if supplied
+ *
+ * @includeFunction ../../test/iterators/IteratorsTest.kt joinConcatenatesTheFirstNElementsAboveAThreshold
+ */
+inline fun <T> java.util.Iterator<T>.join(separator: String, prefix: String = "", postfix: String = "", limit: Int = 20) : String {
+ val buffer = StringBuilder(prefix)
+ var first = true; var count = 0
+ for (element in this) {
+ if (first) first = false else buffer.append(separator)
+ if (++count <= limit) buffer.append(element) else break
+ }
+ if (count > limit) buffer.append("...")
+ return buffer.append(postfix).toString().sure()
}
-*/
/**
- * Returns an iterator which invokes the function to calculate the next value on each iteration until the function returns null
+ * Returns a comma-separated representation of no more than the first 10 elements
+ *
+ * @includeFunction ../../test/iterators/IteratorsTest.kt toStringJoinsNoMoreThanTheFirstTenElements
*/
-inline fun <T> iterate(nextFunction: () -> T?) : Iterator<T> = FunctionIterator(nextFunction)
+inline fun <T> java.util.Iterator<T>.toString(): String = join(separator = ", ", limit = 10)
View
59 libraries/stdlib/test/iterators/IteratorsTest.kt
@@ -0,0 +1,59 @@
+package iterators
+
+import kotlin.test.assertEquals
+import org.junit.Test
+
+class IteratorsTest {
+
+ private fun fibonacci(): java.util.Iterator<Int> {
+ // fibonacci terms
+ var index = 0; var a = 0; var b = 1
+ return iterate<Int> { when (index++) { 0 -> a; 1 -> b; else -> { val result = a + b; a = b; b = result; result } } }
+ }
+
+ Test fun filterAndTakeWhileExtractTheElementsWithinRange() {
+ assertEquals(arrayList(144, 233, 377, 610, 987), fibonacci().filter { it > 100 }.takeWhile { it < 1000 }.toList())
+ }
+
+ Test fun foldReducesTheFirstNElements() {
+ val sum = { (a: Int, b: Int) -> a + b }
+ assertEquals(arrayList(13, 21, 34, 55, 89).fold(0, sum), fibonacci().filter { it > 10 }.take(5).fold(0, sum))
+ }
+
+ Test fun takeExtractsTheFirstNElements() {
+ assertEquals(arrayList(0, 1, 1, 2, 3, 5, 8, 13, 21, 34), fibonacci().take(10).toList())
+ }
+
+ Test fun mapAndTakeWhileExtractTheTransformedElements() {
+ assertEquals(arrayList(0, 3, 3, 6, 9, 15), fibonacci().map { it * 3 }.takeWhile { (i: Int) -> i < 20 }.toList())
+ }
+
+ Test fun flatMapAndTakeExtractTheTransformedElements() {
+ fun intToBinaryDigits() = { (i: Int) ->
+ val binary = Integer.toBinaryString(i).sure()
+ var index = 0
+ iterate<Char> { if (index < binary.length()) binary.get(index++) else null }
+ }
+
+ val expected = arrayList(
+ '0', // fibonacci(0) = 0
+ '1', // fibonacci(1) = 1
+ '1', // fibonacci(2) = 1
+ '1', '0', // fibonacci(3) = 2
+ '1', '1', // fibonacci(4) = 3
+ '1', '0', '1' // fibonacci(5) = 5
+ )
+
+ assertEquals(expected, fibonacci().flatMap<Int, Char>(intToBinaryDigits()).take(10).toList())
+ }
+
+ Test fun joinConcatenatesTheFirstNElementsAboveAThreshold() {
+ assertEquals("13, 21, 34, 55, 89, ...", fibonacci().filter { it > 10 }.join(separator = ", ", limit = 5))
+ }
+
+ Test fun toStringJoinsNoMoreThanTheFirstTenElements() {
+ assertEquals("0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...", fibonacci().toString())
+ assertEquals("13, 21, 34, 55, 89, 144, 233, 377, 610, 987, ...", fibonacci().filter { it > 10 }.toString())
+ assertEquals("144, 233, 377, 610, 987", fibonacci().filter { it > 100 }.takeWhile { it < 1000 }.toString())
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.