diff --git a/js/js.libraries/src/core/generated/_CollectionsJs.kt b/js/js.libraries/src/core/generated/_CollectionsJs.kt index 047194d88eb..c062a0e4786 100644 --- a/js/js.libraries/src/core/generated/_CollectionsJs.kt +++ b/js/js.libraries/src/core/generated/_CollectionsJs.kt @@ -1834,6 +1834,25 @@ public inline fun Iterable.minusElement(element: T): List { return minus(element) } +@SinceKotlin("1.2") +public fun Iterable.pairwise(): List> { + return pairwise { a, b -> a to b } +} + +@SinceKotlin("1.2") +public inline fun Iterable.pairwise(transform: (a: T, b: T) -> R): List { + val iterator = iterator() + if (!iterator.hasNext()) return emptyList() + val result = mutableListOf() + 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`, diff --git a/js/js.libraries/src/core/generated/_SequencesJs.kt b/js/js.libraries/src/core/generated/_SequencesJs.kt index 31d3bb14bb7..9f243463da4 100644 --- a/js/js.libraries/src/core/generated/_SequencesJs.kt +++ b/js/js.libraries/src/core/generated/_SequencesJs.kt @@ -1391,6 +1391,25 @@ public inline fun Sequence.minusElement(element: T): Sequence { return minus(element) } +@SinceKotlin("1.2") +public fun Sequence.pairwise(): Sequence> { + return pairwise { a, b -> a to b } +} + +@SinceKotlin("1.2") +public fun Sequence.pairwise(transform: (a: T, b: T) -> R): Sequence { + 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`, diff --git a/js/js.libraries/src/core/generated/_StringsJs.kt b/js/js.libraries/src/core/generated/_StringsJs.kt index 356ad697cde..aa80052ceca 100644 --- a/js/js.libraries/src/core/generated/_StringsJs.kt +++ b/js/js.libraries/src/core/generated/_StringsJs.kt @@ -1102,6 +1102,22 @@ public inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double { return sum } +@SinceKotlin("1.2") +public fun CharSequence.pairwise(): List> { + return pairwise { a, b -> a to b } +} + +@SinceKotlin("1.2") +public inline fun CharSequence.pairwise(transform: (a: Char, b: Char) -> R): List { + val size = length - 1 + if (size < 1) return emptyList() + val result = ArrayList(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`, diff --git a/libraries/stdlib/common/src/generated/_Collections.kt b/libraries/stdlib/common/src/generated/_Collections.kt index e974fb76822..e4b0fb22e99 100644 --- a/libraries/stdlib/common/src/generated/_Collections.kt +++ b/libraries/stdlib/common/src/generated/_Collections.kt @@ -924,6 +924,12 @@ public expect operator fun Iterable.minus(elements: Sequence): List @kotlin.internal.InlineOnly public expect inline fun Iterable.minusElement(element: T): List +@SinceKotlin("1.2") +public expect fun Iterable.pairwise(): List> + +@SinceKotlin("1.2") +public expect inline fun Iterable.pairwise(transform: (a: T, b: T) -> R): List + /** * Splits the original collection into pair of lists, * where *first* list contains elements for which [predicate] yielded `true`, diff --git a/libraries/stdlib/common/src/generated/_Sequences.kt b/libraries/stdlib/common/src/generated/_Sequences.kt index fc142492987..7d13d1cdf11 100644 --- a/libraries/stdlib/common/src/generated/_Sequences.kt +++ b/libraries/stdlib/common/src/generated/_Sequences.kt @@ -848,6 +848,12 @@ public expect operator fun Sequence.minus(elements: Sequence): Sequenc @kotlin.internal.InlineOnly public expect inline fun Sequence.minusElement(element: T): Sequence +@SinceKotlin("1.2") +public expect fun Sequence.pairwise(): Sequence> + +@SinceKotlin("1.2") +public expect fun Sequence.pairwise(transform: (a: T, b: T) -> R): Sequence + /** * Splits the original sequence into pair of lists, * where *first* list contains elements for which [predicate] yielded `true`, diff --git a/libraries/stdlib/common/src/generated/_Strings.kt b/libraries/stdlib/common/src/generated/_Strings.kt index b6d70155717..4226de31eda 100644 --- a/libraries/stdlib/common/src/generated/_Strings.kt +++ b/libraries/stdlib/common/src/generated/_Strings.kt @@ -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> + +@SinceKotlin("1.2") +public expect inline fun CharSequence.pairwise(transform: (a: Char, b: Char) -> R): List + /** * Splits the original char sequence into pair of char sequences, * where *first* char sequence contains characters for which [predicate] yielded `true`, diff --git a/libraries/stdlib/src/generated/_Collections.kt b/libraries/stdlib/src/generated/_Collections.kt index 63f0fed44e6..48f00224014 100644 --- a/libraries/stdlib/src/generated/_Collections.kt +++ b/libraries/stdlib/src/generated/_Collections.kt @@ -1844,6 +1844,25 @@ public inline fun Iterable.minusElement(element: T): List { return minus(element) } +@SinceKotlin("1.2") +public fun Iterable.pairwise(): List> { + return pairwise { a, b -> a to b } +} + +@SinceKotlin("1.2") +public inline fun Iterable.pairwise(transform: (a: T, b: T) -> R): List { + val iterator = iterator() + if (!iterator.hasNext()) return emptyList() + val result = mutableListOf() + 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`, diff --git a/libraries/stdlib/src/generated/_Sequences.kt b/libraries/stdlib/src/generated/_Sequences.kt index 246947e234d..fb803cc88ef 100644 --- a/libraries/stdlib/src/generated/_Sequences.kt +++ b/libraries/stdlib/src/generated/_Sequences.kt @@ -1413,6 +1413,25 @@ public inline fun Sequence.minusElement(element: T): Sequence { return minus(element) } +@SinceKotlin("1.2") +public fun Sequence.pairwise(): Sequence> { + return pairwise { a, b -> a to b } +} + +@SinceKotlin("1.2") +public fun Sequence.pairwise(transform: (a: T, b: T) -> R): Sequence { + 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`, diff --git a/libraries/stdlib/src/generated/_Strings.kt b/libraries/stdlib/src/generated/_Strings.kt index b2adb7cf962..f28676a9bf4 100644 --- a/libraries/stdlib/src/generated/_Strings.kt +++ b/libraries/stdlib/src/generated/_Strings.kt @@ -1110,6 +1110,22 @@ public inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double { return sum } +@SinceKotlin("1.2") +public fun CharSequence.pairwise(): List> { + return pairwise { a, b -> a to b } +} + +@SinceKotlin("1.2") +public inline fun CharSequence.pairwise(transform: (a: Char, b: Char) -> R): List { + val size = length - 1 + if (size < 1) return emptyList() + val result = ArrayList(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`, diff --git a/libraries/stdlib/test/collections/IterableTests.kt b/libraries/stdlib/test/collections/IterableTests.kt index d2397ad858e..4868830eb39 100644 --- a/libraries/stdlib/test/collections/IterableTests.kt +++ b/libraries/stdlib/test/collections/IterableTests.kt @@ -116,6 +116,25 @@ abstract class OrderedIterableTests>(createFrom: (Array 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>(val createFrom: (Array) -> T, val empty: T) { diff --git a/libraries/stdlib/test/collections/SequenceTest.kt b/libraries/stdlib/test/collections/SequenceTest.kt index 493baf15ec6..7bbf2b50495 100644 --- a/libraries/stdlib/test/collections/SequenceTest.kt +++ b/libraries/stdlib/test/collections/SequenceTest.kt @@ -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 { public class SequenceTest { + private class TriggerSequence(val source: Sequence) : Sequence { + var iterated: Boolean = false + private set + + override fun iterator(): Iterator = source.iterator().also { iterated = true } + } + + fun ensureIsIntermediate(source: Sequence, operation: (Sequence) -> 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(), sequenceOf())) { 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> = sequenceOf("a", "b", "c", "d").pairwise() + assertEquals(listOf("a" to "b", "b" to "c", "c" to "d"), pairs.toList()) + + assertTrue(emptySequence().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() diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index 74246583554..b151f68dc7c 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -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 }) diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 23342e1d11f..a537eb5a64d 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -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; diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt index 63842327512..96821f6d189 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt @@ -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; diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt index fc788323751..ca8756037b0 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt @@ -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 { } } + templates add f("pairwise(transform: (a: T, b: T) -> R)") { + since("1.2") + only(Iterables, Sequences, CharSequences) + typeParam("R") + + returns("List") + inline(true) + body { + """ + val iterator = iterator() + if (!iterator.hasNext()) return emptyList() + val result = mutableListOf() + 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(size) + for (index in 0..size - 1) { + result.add(transform(this[index], this[index + 1])) + } + return result + """ + + } + inline(false, Sequences) + returns(Sequences) { "Sequence" } + 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>") + returns(Sequences) { "Sequence>" } + body { + "return pairwise { a, b -> a to b }" + } + } + templates add f("zip(other: Iterable, transform: (a: T, b: R) -> V)") { exclude(Sequences) doc {