Introduce 'pairwise' function, KEEP-11

This commit is contained in:
Ilya Gorbunov
2017-04-26 23:49:11 +03:00
parent b73be50e5b
commit c815ccfd6a
15 changed files with 298 additions and 1 deletions
@@ -1834,6 +1834,25 @@ public inline fun <T> Iterable<T>.minusElement(element: T): List<T> {
return minus(element)
}
@SinceKotlin("1.2")
public fun <T> Iterable<T>.pairwise(): List<Pair<T, T>> {
return pairwise { a, b -> a to b }
}
@SinceKotlin("1.2")
public inline fun <T, R> Iterable<T>.pairwise(transform: (a: T, b: T) -> R): List<R> {
val iterator = iterator()
if (!iterator.hasNext()) return emptyList()
val result = mutableListOf<R>()
var current = iterator.next()
while (iterator.hasNext()) {
val next = iterator.next()
result.add(transform(current, next))
current = next
}
return result
}
/**
* Splits the original collection into pair of lists,
* where *first* list contains elements for which [predicate] yielded `true`,
@@ -1391,6 +1391,25 @@ public inline fun <T> Sequence<T>.minusElement(element: T): Sequence<T> {
return minus(element)
}
@SinceKotlin("1.2")
public fun <T> Sequence<T>.pairwise(): Sequence<Pair<T, T>> {
return pairwise { a, b -> a to b }
}
@SinceKotlin("1.2")
public fun <T, R> Sequence<T>.pairwise(transform: (a: T, b: T) -> R): Sequence<R> {
return buildSequence result@ {
val iterator = iterator()
if (!iterator.hasNext()) return@result
var current = iterator.next()
while (iterator.hasNext()) {
val next = iterator.next()
yield(transform(current, next))
current = next
}
}
}
/**
* Splits the original sequence into pair of lists,
* where *first* list contains elements for which [predicate] yielded `true`,
@@ -1102,6 +1102,22 @@ public inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double {
return sum
}
@SinceKotlin("1.2")
public fun CharSequence.pairwise(): List<Pair<Char, Char>> {
return pairwise { a, b -> a to b }
}
@SinceKotlin("1.2")
public inline fun <R> CharSequence.pairwise(transform: (a: Char, b: Char) -> R): List<R> {
val size = length - 1
if (size < 1) return emptyList()
val result = ArrayList<R>(size)
for (index in 0..size - 1) {
result.add(transform(this[index], this[index + 1]))
}
return result
}
/**
* Splits the original char sequence into pair of char sequences,
* where *first* char sequence contains characters for which [predicate] yielded `true`,
@@ -924,6 +924,12 @@ public expect operator fun <T> Iterable<T>.minus(elements: Sequence<T>): List<T>
@kotlin.internal.InlineOnly
public expect inline fun <T> Iterable<T>.minusElement(element: T): List<T>
@SinceKotlin("1.2")
public expect fun <T> Iterable<T>.pairwise(): List<Pair<T, T>>
@SinceKotlin("1.2")
public expect inline fun <T, R> Iterable<T>.pairwise(transform: (a: T, b: T) -> R): List<R>
/**
* Splits the original collection into pair of lists,
* where *first* list contains elements for which [predicate] yielded `true`,
@@ -848,6 +848,12 @@ public expect operator fun <T> Sequence<T>.minus(elements: Sequence<T>): Sequenc
@kotlin.internal.InlineOnly
public expect inline fun <T> Sequence<T>.minusElement(element: T): Sequence<T>
@SinceKotlin("1.2")
public expect fun <T> Sequence<T>.pairwise(): Sequence<Pair<T, T>>
@SinceKotlin("1.2")
public expect fun <T, R> Sequence<T>.pairwise(transform: (a: T, b: T) -> R): Sequence<R>
/**
* Splits the original sequence into pair of lists,
* where *first* list contains elements for which [predicate] yielded `true`,
@@ -638,6 +638,12 @@ public expect inline fun CharSequence.sumBy(selector: (Char) -> Int): Int
*/
public expect inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double
@SinceKotlin("1.2")
public expect fun CharSequence.pairwise(): List<Pair<Char, Char>>
@SinceKotlin("1.2")
public expect inline fun <R> CharSequence.pairwise(transform: (a: Char, b: Char) -> R): List<R>
/**
* Splits the original char sequence into pair of char sequences,
* where *first* char sequence contains characters for which [predicate] yielded `true`,
@@ -1844,6 +1844,25 @@ public inline fun <T> Iterable<T>.minusElement(element: T): List<T> {
return minus(element)
}
@SinceKotlin("1.2")
public fun <T> Iterable<T>.pairwise(): List<Pair<T, T>> {
return pairwise { a, b -> a to b }
}
@SinceKotlin("1.2")
public inline fun <T, R> Iterable<T>.pairwise(transform: (a: T, b: T) -> R): List<R> {
val iterator = iterator()
if (!iterator.hasNext()) return emptyList()
val result = mutableListOf<R>()
var current = iterator.next()
while (iterator.hasNext()) {
val next = iterator.next()
result.add(transform(current, next))
current = next
}
return result
}
/**
* Splits the original collection into pair of lists,
* where *first* list contains elements for which [predicate] yielded `true`,
@@ -1413,6 +1413,25 @@ public inline fun <T> Sequence<T>.minusElement(element: T): Sequence<T> {
return minus(element)
}
@SinceKotlin("1.2")
public fun <T> Sequence<T>.pairwise(): Sequence<Pair<T, T>> {
return pairwise { a, b -> a to b }
}
@SinceKotlin("1.2")
public fun <T, R> Sequence<T>.pairwise(transform: (a: T, b: T) -> R): Sequence<R> {
return buildSequence result@ {
val iterator = iterator()
if (!iterator.hasNext()) return@result
var current = iterator.next()
while (iterator.hasNext()) {
val next = iterator.next()
yield(transform(current, next))
current = next
}
}
}
/**
* Splits the original sequence into pair of lists,
* where *first* list contains elements for which [predicate] yielded `true`,
@@ -1110,6 +1110,22 @@ public inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double {
return sum
}
@SinceKotlin("1.2")
public fun CharSequence.pairwise(): List<Pair<Char, Char>> {
return pairwise { a, b -> a to b }
}
@SinceKotlin("1.2")
public inline fun <R> CharSequence.pairwise(transform: (a: Char, b: Char) -> R): List<R> {
val size = length - 1
if (size < 1) return emptyList()
val result = ArrayList<R>(size)
for (index in 0..size - 1) {
result.add(transform(this[index], this[index + 1]))
}
return result
}
/**
* Splits the original char sequence into pair of char sequences,
* where *first* char sequence contains characters for which [predicate] yielded `true`,
@@ -116,6 +116,25 @@ abstract class OrderedIterableTests<T : Iterable<String>>(createFrom: (Array<out
expect(null) { empty.lastOrNull() }
expect("foo") { data.lastOrNull { it.startsWith("f") } }
}
@Test
fun pairwise() {
val data = createFrom("", "a", "xyz")
val lengthDeltas = data.pairwise { a, b -> b.length - a.length }
assertEquals(listOf(1, 2), lengthDeltas)
assertTrue(empty.pairwise { a, b -> a + b }.isEmpty())
assertTrue(createFrom("foo").pairwise { a, b -> a + b }.isEmpty())
}
@Test
fun pairwisePairs() {
assertTrue(empty.pairwise().isEmpty())
assertTrue(createFrom("foo").pairwise().isEmpty())
assertEquals(listOf("a" to "b"), createFrom("a", "b").pairwise())
assertEquals(listOf("a" to "b", "b" to "c"), createFrom("a", "b", "c").pairwise())
}
}
abstract class IterableTests<T : Iterable<String>>(val createFrom: (Array<out String>) -> T, val empty: T) {
@@ -1,3 +1,19 @@
/*
* Copyright 2010-2017 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 test.collections
import org.junit.Test
@@ -12,6 +28,22 @@ fun fibonacci(): Sequence<Int> {
public class SequenceTest {
private class TriggerSequence<out T>(val source: Sequence<T>) : Sequence<T> {
var iterated: Boolean = false
private set
override fun iterator(): Iterator<T> = source.iterator().also { iterated = true }
}
fun <T> ensureIsIntermediate(source: Sequence<T>, operation: (Sequence<T>) -> Sequence<*>) {
TriggerSequence(source).let { s ->
val result = operation(s)
assertFalse(s.iterated, "Source should not be iterated before the result is")
result.iterator().hasNext()
assertTrue(s.iterated, "Source should be iterated after the result is iterated")
}
}
@Test fun filterEmptySequence() {
for (sequence in listOf(emptySequence<String>(), sequenceOf<String>())) {
assertEquals(0, sequence.filter { false }.count())
@@ -165,6 +197,25 @@ public class SequenceTest {
assertEquals("", sequenceOf(1).dropWhile { it < 200 }.joinToString(limit = 10))
}
@Test fun pairwise() {
val deltas = fibonacci().pairwise { a, b -> b - a }
// deltas of 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, ...
// is the same sequence prepended by 1
assertEquals(listOf(1) + fibonacci().take(9), deltas.take(10).toList())
ensureIsIntermediate(source = sequenceOf(1, 2)) { it.pairwise { a, b -> b - a } }
}
@Test fun pairwisePairs() {
val pairs: Sequence<Pair<String, String>> = sequenceOf("a", "b", "c", "d").pairwise()
assertEquals(listOf("a" to "b", "b" to "c", "c" to "d"), pairs.toList())
assertTrue(emptySequence<String>().pairwise().toList().isEmpty())
assertTrue(sequenceOf(1).pairwise().toList().isEmpty())
ensureIsIntermediate(source = sequenceOf(1, 2)) { it.pairwise() }
}
@Test fun zip() {
expect(listOf("ab", "bc", "cd")) {
sequenceOf("a", "b", "c").zip(sequenceOf("b", "c", "d")) { a, b -> a + b }.toList()
+13 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* Copyright 2010-2017 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.
@@ -837,6 +837,18 @@ class StringTest {
assertContentEquals("abc", pair.second, "pair.second")
}
@Test fun pairwise() = withOneCharSequenceArg { arg1 ->
assertEquals(listOf("ab", "bc"), arg1("abc").pairwise { a, b -> a.toString() + b })
assertTrue(arg1("").pairwise { a, b -> a.toString() + b }.isEmpty())
assertTrue(arg1("a").pairwise { a, b -> a.toString() + b }.isEmpty())
}
@Test fun pairwisePairs() = withOneCharSequenceArg { arg1 ->
assertEquals(listOf('a' to 'b', 'b' to 'c'), arg1("abc").pairwise())
assertTrue(arg1("").pairwise().isEmpty())
assertTrue(arg1("a").pairwise().isEmpty())
}
@Test fun map() = withOneCharSequenceArg { arg1 ->
assertEquals(listOf('a', 'b', 'c'), arg1("abc").map { it })
@@ -1666,6 +1666,8 @@ public final class kotlin/collections/CollectionsKt {
public static final fun none (Ljava/lang/Iterable;)Z
public static final fun none (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z
public static final fun onEach (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/lang/Iterable;
public static final fun pairwise (Ljava/lang/Iterable;)Ljava/util/List;
public static final fun pairwise (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Ljava/util/List;
public static final fun partition (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
public static final fun plus (Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/List;
public static final fun plus (Ljava/lang/Iterable;Ljava/lang/Object;)Ljava/util/List;
@@ -3551,6 +3553,8 @@ public final class kotlin/sequences/SequencesKt {
public static final fun none (Lkotlin/sequences/Sequence;)Z
public static final fun none (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Z
public static final fun onEach (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
public static final fun pairwise (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence;
public static final fun pairwise (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence;
public static final fun partition (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
public static final fun plus (Lkotlin/sequences/Sequence;Ljava/lang/Iterable;)Lkotlin/sequences/Sequence;
public static final fun plus (Lkotlin/sequences/Sequence;Ljava/lang/Object;)Lkotlin/sequences/Sequence;
@@ -3910,6 +3914,8 @@ public final class kotlin/text/StringsKt {
public static final fun padStart (Ljava/lang/String;IC)Ljava/lang/String;
public static synthetic fun padStart$default (Ljava/lang/CharSequence;ICILjava/lang/Object;)Ljava/lang/CharSequence;
public static synthetic fun padStart$default (Ljava/lang/String;ICILjava/lang/Object;)Ljava/lang/String;
public static final fun pairwise (Ljava/lang/CharSequence;)Ljava/util/List;
public static final fun pairwise (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function2;)Ljava/util/List;
public static final fun partition (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
public static final fun partition (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
public static final fun prependIndent (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
@@ -1525,6 +1525,8 @@ public final class kotlin/collections/CollectionsKt {
public static final fun none (Ljava/lang/Iterable;)Z
public static final fun none (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z
public static final fun onEach (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/lang/Iterable;
public static final fun pairwise (Ljava/lang/Iterable;)Ljava/util/List;
public static final fun pairwise (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Ljava/util/List;
public static final fun partition (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
public static final fun plus (Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/List;
public static final fun plus (Ljava/lang/Iterable;Ljava/lang/Object;)Ljava/util/List;
@@ -2246,6 +2248,8 @@ public final class kotlin/sequences/SequencesKt {
public static final fun none (Lkotlin/sequences/Sequence;)Z
public static final fun none (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Z
public static final fun onEach (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
public static final fun pairwise (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence;
public static final fun pairwise (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence;
public static final fun partition (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
public static final fun plus (Lkotlin/sequences/Sequence;Ljava/lang/Iterable;)Lkotlin/sequences/Sequence;
public static final fun plus (Lkotlin/sequences/Sequence;Ljava/lang/Object;)Lkotlin/sequences/Sequence;
@@ -2605,6 +2609,8 @@ public final class kotlin/text/StringsKt {
public static final fun padStart (Ljava/lang/String;IC)Ljava/lang/String;
public static synthetic fun padStart$default (Ljava/lang/CharSequence;ICILjava/lang/Object;)Ljava/lang/CharSequence;
public static synthetic fun padStart$default (Ljava/lang/String;ICILjava/lang/Object;)Ljava/lang/String;
public static final fun pairwise (Ljava/lang/CharSequence;)Ljava/util/List;
public static final fun pairwise (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function2;)Ljava/util/List;
public static final fun partition (Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
public static final fun partition (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
public static final fun prependIndent (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
@@ -1,3 +1,19 @@
/*
* Copyright 2010-2017 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 templates
import templates.Family.*
@@ -543,6 +559,67 @@ fun generators(): List<GenericFunction> {
}
}
templates add f("pairwise(transform: (a: T, b: T) -> R)") {
since("1.2")
only(Iterables, Sequences, CharSequences)
typeParam("R")
returns("List<R>")
inline(true)
body {
"""
val iterator = iterator()
if (!iterator.hasNext()) return emptyList()
val result = mutableListOf<R>()
var current = iterator.next()
while (iterator.hasNext()) {
val next = iterator.next()
result.add(transform(current, next))
current = next
}
return result
"""
}
body(CharSequences) { f ->
"""
val size = ${if (f == CharSequences) "length" else "size" } - 1
if (size < 1) return emptyList()
val result = ArrayList<R>(size)
for (index in 0..size - 1) {
result.add(transform(this[index], this[index + 1]))
}
return result
"""
}
inline(false, Sequences)
returns(Sequences) { "Sequence<R>" }
body(Sequences) {
"""
return buildSequence result@ {
val iterator = iterator()
if (!iterator.hasNext()) return@result
var current = iterator.next()
while (iterator.hasNext()) {
val next = iterator.next()
yield(transform(current, next))
current = next
}
}
"""
}
}
templates add f("pairwise()") {
since("1.2")
only(Iterables, Sequences, CharSequences)
returns("List<Pair<T, T>>")
returns(Sequences) { "Sequence<Pair<T, T>>" }
body {
"return pairwise { a, b -> a to b }"
}
}
templates add f("zip(other: Iterable<R>, transform: (a: T, b: R) -> V)") {
exclude(Sequences)
doc {