From 559255f38e435066b317dd6a8f325280294cdb3f Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 27 Jul 2017 00:57:58 +0300 Subject: [PATCH] windowed function: add default parameters, drop or keep partial windows, KEEP-11 Make step default to 1. Add partialWindows boolean parameter defaulting to false to control whether to keep partial windows in the end. --- .../src/core/generated/_CollectionsJs.kt | 25 ++++++--- .../src/core/generated/_SequencesJs.kt | 20 ++++--- .../src/core/generated/_StringsJs.kt | 41 +++++++++----- .../common/src/generated/_Collections.kt | 51 +++++++++++++++-- .../stdlib/common/src/generated/_Sequences.kt | 16 ++++-- .../stdlib/common/src/generated/_Strings.kt | 44 ++++++++++++--- .../test/samples/collections/sequences.kt | 9 ++- .../stdlib/src/generated/_Collections.kt | 25 ++++++--- libraries/stdlib/src/generated/_Sequences.kt | 20 ++++--- libraries/stdlib/src/generated/_Strings.kt | 41 +++++++++----- .../src/kotlin/collections/SlidingWindow.kt | 10 ++-- .../stdlib/test/collections/IterableTests.kt | 23 ++++++-- .../stdlib/test/collections/SequenceTest.kt | 23 ++++++-- libraries/stdlib/test/text/StringTest.kt | 26 ++++++--- .../kotlin-stdlib-runtime-merged.txt | 24 +++++--- .../reference-public-api/kotlin-stdlib.txt | 24 +++++--- .../src/templates/Generators.kt | 56 ++++++++++++------- 17 files changed, 336 insertions(+), 142 deletions(-) diff --git a/js/js.libraries/src/core/generated/_CollectionsJs.kt b/js/js.libraries/src/core/generated/_CollectionsJs.kt index b291545a76e..ae9beb21afb 100644 --- a/js/js.libraries/src/core/generated/_CollectionsJs.kt +++ b/js/js.libraries/src/core/generated/_CollectionsJs.kt @@ -1799,7 +1799,7 @@ public fun List.requireNoNulls(): List { */ @SinceKotlin("1.2") public fun Iterable.chunked(size: Int): List> { - return windowed(size, size) + return windowed(size, size, partialWindows = true) } /** @@ -1818,7 +1818,7 @@ public fun Iterable.chunked(size: Int): List> { */ @SinceKotlin("1.2") public fun Iterable.chunked(size: Int, transform: (List) -> R): List { - return windowed(size, size, transform) + return windowed(size, size, partialWindows = true, transform = transform) } /** @@ -2011,25 +2011,29 @@ public inline fun Collection.plusElement(element: T): List { * * Both [size] and [step] must be positive and can be greater than the number of elements in this collection. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public fun Iterable.windowed(size: Int, step: Int): List> { +public fun Iterable.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): List> { checkWindowSizeStep(size, step) if (this is RandomAccess && this is List) { val thisSize = this.size val result = ArrayList>((thisSize + step - 1) / step) var index = 0 while (index < thisSize) { - result.add(List(size.coerceAtMost(thisSize - index)) { this[it + index] }) + val windowSize = size.coerceAtMost(thisSize - index) + if (windowSize < size && !partialWindows) break + result.add(List(windowSize) { this[it + index] }) index += step } return result } val result = ArrayList>() - windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = false).forEach { + windowedIterator(iterator(), size, step, partialWindows, reuseBuffer = false).forEach { result.add(it) } return result @@ -2046,12 +2050,14 @@ public fun Iterable.windowed(size: Int, step: Int): List> { * * Both [size] and [step] must be positive and can be greater than the number of elements in this collection. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public fun Iterable.windowed(size: Int, step: Int, transform: (List) -> R): List { +public fun Iterable.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (List) -> R): List { checkWindowSizeStep(size, step) if (this is RandomAccess && this is List) { val thisSize = this.size @@ -2060,13 +2066,14 @@ public fun Iterable.windowed(size: Int, step: Int, transform: (List var index = 0 while (index < thisSize) { window.move(index, (index + size).coerceAtMost(thisSize)) + if (!partialWindows && window.size < size) break result.add(transform(window)) index += step } return result } val result = ArrayList() - windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = true).forEach { + windowedIterator(iterator(), size, step, partialWindows, reuseBuffer = true).forEach { result.add(transform(it)) } return result diff --git a/js/js.libraries/src/core/generated/_SequencesJs.kt b/js/js.libraries/src/core/generated/_SequencesJs.kt index aa2edb099c7..76c38b5da2b 100644 --- a/js/js.libraries/src/core/generated/_SequencesJs.kt +++ b/js/js.libraries/src/core/generated/_SequencesJs.kt @@ -1322,7 +1322,7 @@ public fun Sequence.requireNoNulls(): Sequence { */ @SinceKotlin("1.2") public fun Sequence.chunked(size: Int): Sequence> { - return windowed(size, size) + return windowed(size, size, partialWindows = true) } /** @@ -1343,7 +1343,7 @@ public fun Sequence.chunked(size: Int): Sequence> { */ @SinceKotlin("1.2") public fun Sequence.chunked(size: Int, transform: (List) -> R): Sequence { - return windowed(size, size, transform) + return windowed(size, size, partialWindows = true, transform = transform) } /** @@ -1524,13 +1524,15 @@ public inline fun Sequence.plusElement(element: T): Sequence { * * Both [size] and [step] must be positive and can be greater than the number of elements in this sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public fun Sequence.windowed(size: Int, step: Int): Sequence> { - return windowedSequence(size, step, dropTrailing = false, reuseBuffer = false) +public fun Sequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): Sequence> { + return windowedSequence(size, step, partialWindows, reuseBuffer = false) } /** @@ -1544,13 +1546,15 @@ public fun Sequence.windowed(size: Int, step: Int): Sequence> { * * Both [size] and [step] must be positive and can be greater than the number of elements in this sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public fun Sequence.windowed(size: Int, step: Int, transform: (List) -> R): Sequence { - return windowedSequence(size, step, dropTrailing = false, reuseBuffer = true).map(transform) +public fun Sequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (List) -> R): Sequence { + return windowedSequence(size, step, partialWindows, reuseBuffer = true).map(transform) } /** diff --git a/js/js.libraries/src/core/generated/_StringsJs.kt b/js/js.libraries/src/core/generated/_StringsJs.kt index 7e241ef91dc..c71f0e5614e 100644 --- a/js/js.libraries/src/core/generated/_StringsJs.kt +++ b/js/js.libraries/src/core/generated/_StringsJs.kt @@ -1113,7 +1113,7 @@ public inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double { */ @SinceKotlin("1.2") public fun CharSequence.chunked(size: Int): List { - return windowed(size, size) + return windowed(size, size, partialWindows = true) } /** @@ -1132,7 +1132,7 @@ public fun CharSequence.chunked(size: Int): List { */ @SinceKotlin("1.2") public fun CharSequence.chunked(size: Int, transform: (CharSequence) -> R): List { - return windowed(size, size, transform) + return windowed(size, size, partialWindows = true, transform = transform) } /** @@ -1165,7 +1165,7 @@ public fun CharSequence.chunkedSequence(size: Int): Sequence { */ @SinceKotlin("1.2") public fun CharSequence.chunkedSequence(size: Int, transform: (CharSequence) -> R): Sequence { - return windowedSequence(size, size, transform) + return windowedSequence(size, size, partialWindows = true, transform = transform) } @Deprecated("Use zipWithNext instead", ReplaceWith("zipWithNext()")) @@ -1225,13 +1225,15 @@ public inline fun String.partition(predicate: (Char) -> Boolean): Pair { - return windowed(size, step) { it.toString() } +public fun CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): List { + return windowed(size, step, partialWindows) { it.toString() } } /** @@ -1245,18 +1247,22 @@ public fun CharSequence.windowed(size: Int, step: Int): List { * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public fun CharSequence.windowed(size: Int, step: Int, transform: (CharSequence) -> R): List { +public fun CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): List { checkWindowSizeStep(size, step) val thisSize = this.length val result = ArrayList((thisSize + step - 1) / step) var index = 0 while (index < thisSize) { - result.add(transform(subSequence(index, (index + size).coerceAtMost(thisSize)))) + val end = index + size + val coercedEnd = if (end > thisSize) { if (partialWindows) thisSize else break } else end + result.add(transform(subSequence(index, coercedEnd))) index += step } return result @@ -1271,13 +1277,15 @@ public fun CharSequence.windowed(size: Int, step: Int, transform: (CharSeque * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public fun CharSequence.windowedSequence(size: Int, step: Int): Sequence { - return windowedSequence(size, step) { it.toString() } +public fun CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false): Sequence { + return windowedSequence(size, step, partialWindows) { it.toString() } } /** @@ -1291,14 +1299,17 @@ public fun CharSequence.windowedSequence(size: Int, step: Int): Sequence * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public fun CharSequence.windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R): Sequence { +public fun CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): Sequence { checkWindowSizeStep(size, step) - return (indices step step).asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) } + val windows = (if (partialWindows) indices else 0 until length - size + 1) step step + return windows.asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) } } /** diff --git a/libraries/stdlib/common/src/generated/_Collections.kt b/libraries/stdlib/common/src/generated/_Collections.kt index 622468532b3..486a16c2993 100644 --- a/libraries/stdlib/common/src/generated/_Collections.kt +++ b/libraries/stdlib/common/src/generated/_Collections.kt @@ -1029,12 +1029,33 @@ public expect inline fun Collection.plusElement(element: T): List * * Both [size] and [step] must be positive and can be greater than the number of elements in this collection. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public expect fun Iterable.windowed(size: Int, step: Int): List> +public fun Iterable.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): List> { + checkWindowSizeStep(size, step) + if (this is RandomAccess && this is List) { + val thisSize = this.size + val result = ArrayList>((thisSize + step - 1) / step) + var index = 0 + while (index < thisSize) { + val windowSize = size.coerceAtMost(thisSize - index) + if (windowSize < size && !partialWindows) break + result.add(List(windowSize) { this[it + index] }) + index += step + } + return result + } + val result = ArrayList>() + windowedIterator(iterator(), size, step, partialWindows, reuseBuffer = false).forEach { + result.add(it) + } + return result +} /** * Returns a list of results of applying the given [transform] function to @@ -1047,12 +1068,34 @@ public expect fun Iterable.windowed(size: Int, step: Int): List> * * Both [size] and [step] must be positive and can be greater than the number of elements in this collection. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public expect fun Iterable.windowed(size: Int, step: Int, transform: (List) -> R): List +public fun Iterable.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (List) -> R): List { + checkWindowSizeStep(size, step) + if (this is RandomAccess && this is List) { + val thisSize = this.size + val result = ArrayList((thisSize + step - 1) / step) + val window = MovingSubList(this) + var index = 0 + while (index < thisSize) { + window.move(index, (index + size).coerceAtMost(thisSize)) + if (!partialWindows && window.size < size) break + result.add(transform(window)) + index += step + } + return result + } + val result = ArrayList() + windowedIterator(iterator(), size, step, partialWindows, reuseBuffer = true).forEach { + result.add(transform(it)) + } + return result +} /** * Returns a list of pairs built from elements of both collections with same indexes. List has length of shortest collection. diff --git a/libraries/stdlib/common/src/generated/_Sequences.kt b/libraries/stdlib/common/src/generated/_Sequences.kt index 5125f7fa3fc..7bfbaa5cbae 100644 --- a/libraries/stdlib/common/src/generated/_Sequences.kt +++ b/libraries/stdlib/common/src/generated/_Sequences.kt @@ -952,12 +952,16 @@ public expect inline fun Sequence.plusElement(element: T): Sequence * * Both [size] and [step] must be positive and can be greater than the number of elements in this sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public expect fun Sequence.windowed(size: Int, step: Int): Sequence> +public fun Sequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): Sequence> { + return windowedSequence(size, step, partialWindows, reuseBuffer = false) +} /** * Returns a sequence of results of applying the given [transform] function to @@ -970,12 +974,16 @@ public expect fun Sequence.windowed(size: Int, step: Int): Sequence Sequence.windowed(size: Int, step: Int, transform: (List) -> R): Sequence +public fun Sequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (List) -> R): Sequence { + return windowedSequence(size, step, partialWindows, reuseBuffer = true).map(transform) +} /** * Returns a sequence of pairs built from elements of both sequences with same indexes. diff --git a/libraries/stdlib/common/src/generated/_Strings.kt b/libraries/stdlib/common/src/generated/_Strings.kt index 5f9e5d4e8c4..03d068825c3 100644 --- a/libraries/stdlib/common/src/generated/_Strings.kt +++ b/libraries/stdlib/common/src/generated/_Strings.kt @@ -727,12 +727,16 @@ public expect inline fun String.partition(predicate: (Char) -> Boolean): Pair +public fun CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): List { + return windowed(size, step, partialWindows) { it.toString() } +} /** * Returns a list of results of applying the given [transform] function to @@ -745,12 +749,26 @@ public expect fun CharSequence.windowed(size: Int, step: Int): List * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public expect fun CharSequence.windowed(size: Int, step: Int, transform: (CharSequence) -> R): List +public fun CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): List { + checkWindowSizeStep(size, step) + val thisSize = this.length + val result = ArrayList((thisSize + step - 1) / step) + var index = 0 + while (index < thisSize) { + val end = index + size + val coercedEnd = if (end > thisSize) { if (partialWindows) thisSize else break } else end + result.add(transform(subSequence(index, coercedEnd))) + index += step + } + return result +} /** * Returns a sequence of snapshots of the window of the given [size] @@ -761,12 +779,16 @@ public expect fun CharSequence.windowed(size: Int, step: Int, transform: (Ch * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public expect fun CharSequence.windowedSequence(size: Int, step: Int): Sequence +public fun CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false): Sequence { + return windowedSequence(size, step, partialWindows) { it.toString() } +} /** * Returns a sequence of results of applying the given [transform] function to @@ -779,12 +801,18 @@ public expect fun CharSequence.windowedSequence(size: Int, step: Int): Sequence< * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public expect fun CharSequence.windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R): Sequence +public fun CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): Sequence { + checkWindowSizeStep(size, step) + val windows = (if (partialWindows) indices else 0 until length - size + 1) step step + return windows.asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) } +} /** * Returns a list of pairs built from characters of both char sequences with same indexes. List has length of shortest char sequence. diff --git a/libraries/stdlib/samples/test/samples/collections/sequences.kt b/libraries/stdlib/samples/test/samples/collections/sequences.kt index 586e48d54be..705e21a0c02 100644 --- a/libraries/stdlib/samples/test/samples/collections/sequences.kt +++ b/libraries/stdlib/samples/test/samples/collections/sequences.kt @@ -153,7 +153,10 @@ class Sequences { val moreSparseWindows = sequence.windowed(size = 5, step = 3) assertPrints(moreSparseWindows.take(4).toList(), "[[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11], [10, 11, 12, 13, 14]]") - val partialWindows = sequence.take(10).windowed(size = 5, step = 3) + val fullWindows = sequence.take(10).windowed(size = 5, step = 3) + assertPrints(fullWindows.toList(), "[[1, 2, 3, 4, 5], [4, 5, 6, 7, 8]]") + + val partialWindows = sequence.take(10).windowed(size = 5, step = 3, partialWindows = true) assertPrints(partialWindows.toList(), "[[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10], [10]]") } @@ -161,10 +164,10 @@ class Sequences { fun averageWindows() { val dataPoints = sequenceOf(10, 15, 18, 25, 19, 21, 14, 8, 5) - val averaged = dataPoints.windowed(size = 4, step = 1) { window -> window.average() } + val averaged = dataPoints.windowed(size = 4, step = 1, partialWindows = true) { window -> window.average() } assertPrints(averaged.toList(), "[17.0, 19.25, 20.75, 19.75, 15.5, 12.0, 9.0, 6.5, 5.0]") - val averagedNoPartialWindows = dataPoints.windowed(size = 4, step = 1).filter { it.size == 4 }.map { it.average() } + val averagedNoPartialWindows = dataPoints.windowed(size = 4, step = 1).map { it.average() } assertPrints(averagedNoPartialWindows.toList(), "[17.0, 19.25, 20.75, 19.75, 15.5, 12.0]") } } diff --git a/libraries/stdlib/src/generated/_Collections.kt b/libraries/stdlib/src/generated/_Collections.kt index 02a77b8b1b7..500fb466be8 100644 --- a/libraries/stdlib/src/generated/_Collections.kt +++ b/libraries/stdlib/src/generated/_Collections.kt @@ -1809,7 +1809,7 @@ public fun List.requireNoNulls(): List { */ @SinceKotlin("1.2") public fun Iterable.chunked(size: Int): List> { - return windowed(size, size) + return windowed(size, size, partialWindows = true) } /** @@ -1828,7 +1828,7 @@ public fun Iterable.chunked(size: Int): List> { */ @SinceKotlin("1.2") public fun Iterable.chunked(size: Int, transform: (List) -> R): List { - return windowed(size, size, transform) + return windowed(size, size, partialWindows = true, transform = transform) } /** @@ -2021,25 +2021,29 @@ public inline fun Collection.plusElement(element: T): List { * * Both [size] and [step] must be positive and can be greater than the number of elements in this collection. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public fun Iterable.windowed(size: Int, step: Int): List> { +public fun Iterable.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): List> { checkWindowSizeStep(size, step) if (this is RandomAccess && this is List) { val thisSize = this.size val result = ArrayList>((thisSize + step - 1) / step) var index = 0 while (index < thisSize) { - result.add(List(size.coerceAtMost(thisSize - index)) { this[it + index] }) + val windowSize = size.coerceAtMost(thisSize - index) + if (windowSize < size && !partialWindows) break + result.add(List(windowSize) { this[it + index] }) index += step } return result } val result = ArrayList>() - windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = false).forEach { + windowedIterator(iterator(), size, step, partialWindows, reuseBuffer = false).forEach { result.add(it) } return result @@ -2056,12 +2060,14 @@ public fun Iterable.windowed(size: Int, step: Int): List> { * * Both [size] and [step] must be positive and can be greater than the number of elements in this collection. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public fun Iterable.windowed(size: Int, step: Int, transform: (List) -> R): List { +public fun Iterable.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (List) -> R): List { checkWindowSizeStep(size, step) if (this is RandomAccess && this is List) { val thisSize = this.size @@ -2070,13 +2076,14 @@ public fun Iterable.windowed(size: Int, step: Int, transform: (List var index = 0 while (index < thisSize) { window.move(index, (index + size).coerceAtMost(thisSize)) + if (!partialWindows && window.size < size) break result.add(transform(window)) index += step } return result } val result = ArrayList() - windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = true).forEach { + windowedIterator(iterator(), size, step, partialWindows, reuseBuffer = true).forEach { result.add(transform(it)) } return result diff --git a/libraries/stdlib/src/generated/_Sequences.kt b/libraries/stdlib/src/generated/_Sequences.kt index e0828c8130d..364d3c79954 100644 --- a/libraries/stdlib/src/generated/_Sequences.kt +++ b/libraries/stdlib/src/generated/_Sequences.kt @@ -1344,7 +1344,7 @@ public fun Sequence.requireNoNulls(): Sequence { */ @SinceKotlin("1.2") public fun Sequence.chunked(size: Int): Sequence> { - return windowed(size, size) + return windowed(size, size, partialWindows = true) } /** @@ -1365,7 +1365,7 @@ public fun Sequence.chunked(size: Int): Sequence> { */ @SinceKotlin("1.2") public fun Sequence.chunked(size: Int, transform: (List) -> R): Sequence { - return windowed(size, size, transform) + return windowed(size, size, partialWindows = true, transform = transform) } /** @@ -1546,13 +1546,15 @@ public inline fun Sequence.plusElement(element: T): Sequence { * * Both [size] and [step] must be positive and can be greater than the number of elements in this sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public fun Sequence.windowed(size: Int, step: Int): Sequence> { - return windowedSequence(size, step, dropTrailing = false, reuseBuffer = false) +public fun Sequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): Sequence> { + return windowedSequence(size, step, partialWindows, reuseBuffer = false) } /** @@ -1566,13 +1568,15 @@ public fun Sequence.windowed(size: Int, step: Int): Sequence> { * * Both [size] and [step] must be positive and can be greater than the number of elements in this sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public fun Sequence.windowed(size: Int, step: Int, transform: (List) -> R): Sequence { - return windowedSequence(size, step, dropTrailing = false, reuseBuffer = true).map(transform) +public fun Sequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (List) -> R): Sequence { + return windowedSequence(size, step, partialWindows, reuseBuffer = true).map(transform) } /** diff --git a/libraries/stdlib/src/generated/_Strings.kt b/libraries/stdlib/src/generated/_Strings.kt index 5b713908a61..99cd3397fc7 100644 --- a/libraries/stdlib/src/generated/_Strings.kt +++ b/libraries/stdlib/src/generated/_Strings.kt @@ -1121,7 +1121,7 @@ public inline fun CharSequence.sumByDouble(selector: (Char) -> Double): Double { */ @SinceKotlin("1.2") public fun CharSequence.chunked(size: Int): List { - return windowed(size, size) + return windowed(size, size, partialWindows = true) } /** @@ -1140,7 +1140,7 @@ public fun CharSequence.chunked(size: Int): List { */ @SinceKotlin("1.2") public fun CharSequence.chunked(size: Int, transform: (CharSequence) -> R): List { - return windowed(size, size, transform) + return windowed(size, size, partialWindows = true, transform = transform) } /** @@ -1173,7 +1173,7 @@ public fun CharSequence.chunkedSequence(size: Int): Sequence { */ @SinceKotlin("1.2") public fun CharSequence.chunkedSequence(size: Int, transform: (CharSequence) -> R): Sequence { - return windowedSequence(size, size, transform) + return windowedSequence(size, size, partialWindows = true, transform = transform) } @Deprecated("Use zipWithNext instead", ReplaceWith("zipWithNext()")) @@ -1233,13 +1233,15 @@ public inline fun String.partition(predicate: (Char) -> Boolean): Pair { - return windowed(size, step) { it.toString() } +public fun CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false): List { + return windowed(size, step, partialWindows) { it.toString() } } /** @@ -1253,18 +1255,22 @@ public fun CharSequence.windowed(size: Int, step: Int): List { * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public fun CharSequence.windowed(size: Int, step: Int, transform: (CharSequence) -> R): List { +public fun CharSequence.windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): List { checkWindowSizeStep(size, step) val thisSize = this.length val result = ArrayList((thisSize + step - 1) / step) var index = 0 while (index < thisSize) { - result.add(transform(subSequence(index, (index + size).coerceAtMost(thisSize)))) + val end = index + size + val coercedEnd = if (end > thisSize) { if (partialWindows) thisSize else break } else end + result.add(transform(subSequence(index, coercedEnd))) index += step } return result @@ -1279,13 +1285,15 @@ public fun CharSequence.windowed(size: Int, step: Int, transform: (CharSeque * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.takeWindows */ @SinceKotlin("1.2") -public fun CharSequence.windowedSequence(size: Int, step: Int): Sequence { - return windowedSequence(size, step) { it.toString() } +public fun CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false): Sequence { + return windowedSequence(size, step, partialWindows) { it.toString() } } /** @@ -1299,14 +1307,17 @@ public fun CharSequence.windowedSequence(size: Int, step: Int): Sequence * * Both [size] and [step] must be positive and can be greater than the number of elements in this char sequence. * @param size the number of elements to take in each window - * @param step the number of elements to move the window forward by on an each step + * @param step the number of elements to move the window forward by on an each step, by default 1 + * @param partialWindows controls whether or not to keep partial windows in the end if any, + * by default `false` which means partial windows won't be preserved * * @sample samples.collections.Sequences.Transformations.averageWindows */ @SinceKotlin("1.2") -public fun CharSequence.windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R): Sequence { +public fun CharSequence.windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R): Sequence { checkWindowSizeStep(size, step) - return (indices step step).asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) } + val windows = (if (partialWindows) indices else 0 until length - size + 1) step step + return windows.asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) } } /** diff --git a/libraries/stdlib/src/kotlin/collections/SlidingWindow.kt b/libraries/stdlib/src/kotlin/collections/SlidingWindow.kt index e7e968b6ae0..fc29541c35d 100644 --- a/libraries/stdlib/src/kotlin/collections/SlidingWindow.kt +++ b/libraries/stdlib/src/kotlin/collections/SlidingWindow.kt @@ -27,12 +27,12 @@ internal fun checkWindowSizeStep(size: Int, step: Int) { } } -internal fun Sequence.windowedSequence(size: Int, step: Int, dropTrailing: Boolean, reuseBuffer: Boolean): Sequence> { +internal fun Sequence.windowedSequence(size: Int, step: Int, partialWindows: Boolean, reuseBuffer: Boolean): Sequence> { checkWindowSizeStep(size, step) - return Sequence { windowedIterator(iterator(), size, step, dropTrailing, reuseBuffer) } + return Sequence { windowedIterator(iterator(), size, step, partialWindows, reuseBuffer) } } -internal fun windowedIterator(iterator: Iterator, size: Int, step: Int, dropTrailing: Boolean, reuseBuffer: Boolean): Iterator> { +internal fun windowedIterator(iterator: Iterator, size: Int, step: Int, partialWindows: Boolean, reuseBuffer: Boolean): Iterator> { if (!iterator.hasNext()) return EmptyIterator return buildIterator> { val gap = step - size @@ -49,7 +49,7 @@ internal fun windowedIterator(iterator: Iterator, size: Int, step: Int, d } } if (buffer.isNotEmpty()) { - if (!dropTrailing || buffer.size == size) yield(buffer) + if (partialWindows || buffer.size == size) yield(buffer) } } else { val buffer = RingBuffer(size) @@ -60,7 +60,7 @@ internal fun windowedIterator(iterator: Iterator, size: Int, step: Int, d buffer.removeFirst(step) } } - if (!dropTrailing) { + if (partialWindows) { while (buffer.size > step) { yield(if (reuseBuffer) buffer else ArrayList(buffer)) buffer.removeFirst(step) diff --git a/libraries/stdlib/test/collections/IterableTests.kt b/libraries/stdlib/test/collections/IterableTests.kt index 7c090a06761..aecd5523583 100644 --- a/libraries/stdlib/test/collections/IterableTests.kt +++ b/libraries/stdlib/test/collections/IterableTests.kt @@ -167,23 +167,36 @@ abstract class OrderedIterableTests>(createFrom: (Array + assertEquals(data.toList(), data.windowed(size, 1).single()) + assertTrue(data.windowed(size + 1, 1).isEmpty()) + + val result3partial = data.windowed(size, 1, partialWindows = true) + result3partial.forEachIndexed { index, window -> assertEquals(size - index, window.size, "size of window#$index") } diff --git a/libraries/stdlib/test/collections/SequenceTest.kt b/libraries/stdlib/test/collections/SequenceTest.kt index 5770c0fa0f0..1aafce5fbc6 100644 --- a/libraries/stdlib/test/collections/SequenceTest.kt +++ b/libraries/stdlib/test/collections/SequenceTest.kt @@ -259,23 +259,36 @@ public class SequenceTest { val seq = infiniteSeq.take(7) val result1 = seq.windowed(4, 2) + assertEquals(listOf( + listOf(0, 1, 2, 3), + listOf(2, 3, 4, 5) + ), result1.toList()) + + val result1partial = seq.windowed(4, 2, partialWindows = true) assertEquals(listOf( listOf(0, 1, 2, 3), listOf(2, 3, 4, 5), listOf(4, 5, 6), listOf(6) - ), result1.toList()) + ), result1partial.toList()) val result2 = seq.windowed(2, 3) { it.joinToString("") } - assertEquals(listOf("01", "34", "6"), result2.toList()) + assertEquals(listOf("01", "34"), result2.toList()) - assertEquals(seq.chunked(2).toList(), seq.windowed(2, 2).toList()) + val result2partial = seq.windowed(2, 3, partialWindows = true) { it.joinToString("") } + assertEquals(listOf("01", "34", "6"), result2partial.toList()) + + assertEquals(seq.chunked(2).toList(), seq.windowed(2, 2, partialWindows = true).toList()) assertEquals(seq.take(2).toList(), seq.windowed(2, size).single()) assertEquals(seq.take(3).toList(), seq.windowed(3, size + 3).single()) - val result3 = seq.windowed(size, 1) - result3.forEachIndexed { index, window -> + + assertEquals(seq.toList(), seq.windowed(size, 1).single()) + assertTrue(seq.windowed(size + 1, 1).none()) + + val result3partial = seq.windowed(size, 1, partialWindows = true) + result3partial.forEachIndexed { index, window -> assertEquals(size - index, window.size, "size of window#$index") } diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index dfaa190a77c..e632533e2b1 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -884,18 +884,27 @@ class StringTest { val size = 7 val data = arg1("abcdefg") val result = data.windowed(4, 2) - assertEquals(listOf("abcd", "cdef", "efg", "g"), result) + assertEquals(listOf("abcd", "cdef"), result) + + val resultPartial = data.windowed(4, 2, partialWindows = true) + assertEquals(listOf("abcd", "cdef", "efg", "g"), resultPartial) val result2 = data.windowed(2, 3) { it.reversed().toString() } - assertEquals(listOf("ba", "ed", "g"), result2) + assertEquals(listOf("ba", "ed"), result2) + val result2partial = data.windowed(2, 3, partialWindows = true) { it.reversed().toString() } + assertEquals(listOf("ba", "ed", "g"), result2partial) - assertEquals(data.chunked(2), data.windowed(2, 2)) + assertEquals(data.chunked(2), data.windowed(2, 2, partialWindows = true)) assertEquals(data.take(2), data.windowed(2, size).single()) assertEquals(data.take(3), data.windowed(3, size + 3).single()) - val result3 = data.windowed(size, 1) - result3.forEachIndexed { index, window -> + + assertEquals(data.toString(), data.windowed(size, 1).single()) + assertTrue(data.windowed(size + 1, 1).isEmpty()) + + val result3partial = data.windowed(size, 1, partialWindows = true) + result3partial.forEachIndexed { index, window -> assertEquals(size - index, window.length, "size of window#$index") } @@ -907,8 +916,11 @@ class StringTest { } for (window in 1..size + 1) { - for (step in 1..size + 1) - compare(data.windowed(window, step).iterator(), data.windowedSequence(window, step).iterator()) { iteratorBehavior() } + for (step in 1..size + 1) { + compare(data.windowed(window, step).iterator(), data.windowedSequence(window, step).iterator()) { iteratorBehavior() } + compare(data.windowed(window, step, partialWindows = true).iterator(), + data.windowedSequence(window, step, partialWindows = true).iterator()) { iteratorBehavior() } + } } } 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 28fc6d56279..57dfbf43e5e 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 @@ -1749,8 +1749,10 @@ public final class kotlin/collections/CollectionsKt { public static final fun toSortedSet (Ljava/lang/Iterable;Ljava/util/Comparator;)Ljava/util/SortedSet; public static final fun union (Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set; public static final fun unzip (Ljava/lang/Iterable;)Lkotlin/Pair; - public static final fun windowed (Ljava/lang/Iterable;II)Ljava/util/List; - public static final fun windowed (Ljava/lang/Iterable;IILkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun windowed (Ljava/lang/Iterable;IIZ)Ljava/util/List; + public static final fun windowed (Ljava/lang/Iterable;IIZLkotlin/jvm/functions/Function1;)Ljava/util/List; + public static synthetic fun windowed$default (Ljava/lang/Iterable;IIZILjava/lang/Object;)Ljava/util/List; + public static synthetic fun windowed$default (Ljava/lang/Iterable;IIZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/util/List; public static final fun withIndex (Ljava/lang/Iterable;)Ljava/lang/Iterable; public static final fun withIndex (Ljava/util/Iterator;)Ljava/util/Iterator; public static final fun zip (Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/List; @@ -3602,8 +3604,10 @@ public final class kotlin/sequences/SequencesKt { public static final fun toSortedSet (Lkotlin/sequences/Sequence;)Ljava/util/SortedSet; public static final fun toSortedSet (Lkotlin/sequences/Sequence;Ljava/util/Comparator;)Ljava/util/SortedSet; public static final fun unzip (Lkotlin/sequences/Sequence;)Lkotlin/Pair; - public static final fun windowed (Lkotlin/sequences/Sequence;II)Lkotlin/sequences/Sequence; - public static final fun windowed (Lkotlin/sequences/Sequence;IILkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static final fun windowed (Lkotlin/sequences/Sequence;IIZ)Lkotlin/sequences/Sequence; + public static final fun windowed (Lkotlin/sequences/Sequence;IIZLkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static synthetic fun windowed$default (Lkotlin/sequences/Sequence;IIZILjava/lang/Object;)Lkotlin/sequences/Sequence; + public static synthetic fun windowed$default (Lkotlin/sequences/Sequence;IIZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/sequences/Sequence; public static final fun withIndex (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun zip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun zip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; @@ -4082,10 +4086,14 @@ public final class kotlin/text/StringsKt { public static final fun trimStart (Ljava/lang/CharSequence;[C)Ljava/lang/CharSequence; public static final fun trimStart (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Ljava/lang/String; public static final fun trimStart (Ljava/lang/String;[C)Ljava/lang/String; - public static final fun windowed (Ljava/lang/CharSequence;II)Ljava/util/List; - public static final fun windowed (Ljava/lang/CharSequence;IILkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun windowedSequence (Ljava/lang/CharSequence;II)Lkotlin/sequences/Sequence; - public static final fun windowedSequence (Ljava/lang/CharSequence;IILkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static final fun windowed (Ljava/lang/CharSequence;IIZ)Ljava/util/List; + public static final fun windowed (Ljava/lang/CharSequence;IIZLkotlin/jvm/functions/Function1;)Ljava/util/List; + public static synthetic fun windowed$default (Ljava/lang/CharSequence;IIZILjava/lang/Object;)Ljava/util/List; + public static synthetic fun windowed$default (Ljava/lang/CharSequence;IIZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/util/List; + public static final fun windowedSequence (Ljava/lang/CharSequence;IIZ)Lkotlin/sequences/Sequence; + public static final fun windowedSequence (Ljava/lang/CharSequence;IIZLkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static synthetic fun windowedSequence$default (Ljava/lang/CharSequence;IIZILjava/lang/Object;)Lkotlin/sequences/Sequence; + public static synthetic fun windowedSequence$default (Ljava/lang/CharSequence;IIZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/sequences/Sequence; public static final fun withIndex (Ljava/lang/CharSequence;)Ljava/lang/Iterable; public static final fun zip (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/util/List; public static final fun zip (Ljava/lang/CharSequence;Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function2;)Ljava/util/List; 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 a9a12889c13..269d28c83ff 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 @@ -1608,8 +1608,10 @@ public final class kotlin/collections/CollectionsKt { public static final fun toSortedSet (Ljava/lang/Iterable;Ljava/util/Comparator;)Ljava/util/SortedSet; public static final fun union (Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set; public static final fun unzip (Ljava/lang/Iterable;)Lkotlin/Pair; - public static final fun windowed (Ljava/lang/Iterable;II)Ljava/util/List; - public static final fun windowed (Ljava/lang/Iterable;IILkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun windowed (Ljava/lang/Iterable;IIZ)Ljava/util/List; + public static final fun windowed (Ljava/lang/Iterable;IIZLkotlin/jvm/functions/Function1;)Ljava/util/List; + public static synthetic fun windowed$default (Ljava/lang/Iterable;IIZILjava/lang/Object;)Ljava/util/List; + public static synthetic fun windowed$default (Ljava/lang/Iterable;IIZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/util/List; public static final fun withIndex (Ljava/lang/Iterable;)Ljava/lang/Iterable; public static final fun withIndex (Ljava/util/Iterator;)Ljava/util/Iterator; public static final fun zip (Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/List; @@ -2297,8 +2299,10 @@ public final class kotlin/sequences/SequencesKt { public static final fun toSortedSet (Lkotlin/sequences/Sequence;)Ljava/util/SortedSet; public static final fun toSortedSet (Lkotlin/sequences/Sequence;Ljava/util/Comparator;)Ljava/util/SortedSet; public static final fun unzip (Lkotlin/sequences/Sequence;)Lkotlin/Pair; - public static final fun windowed (Lkotlin/sequences/Sequence;II)Lkotlin/sequences/Sequence; - public static final fun windowed (Lkotlin/sequences/Sequence;IILkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static final fun windowed (Lkotlin/sequences/Sequence;IIZ)Lkotlin/sequences/Sequence; + public static final fun windowed (Lkotlin/sequences/Sequence;IIZLkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static synthetic fun windowed$default (Lkotlin/sequences/Sequence;IIZILjava/lang/Object;)Lkotlin/sequences/Sequence; + public static synthetic fun windowed$default (Lkotlin/sequences/Sequence;IIZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/sequences/Sequence; public static final fun withIndex (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun zip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun zip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; @@ -2777,10 +2781,14 @@ public final class kotlin/text/StringsKt { public static final fun trimStart (Ljava/lang/CharSequence;[C)Ljava/lang/CharSequence; public static final fun trimStart (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Ljava/lang/String; public static final fun trimStart (Ljava/lang/String;[C)Ljava/lang/String; - public static final fun windowed (Ljava/lang/CharSequence;II)Ljava/util/List; - public static final fun windowed (Ljava/lang/CharSequence;IILkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun windowedSequence (Ljava/lang/CharSequence;II)Lkotlin/sequences/Sequence; - public static final fun windowedSequence (Ljava/lang/CharSequence;IILkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static final fun windowed (Ljava/lang/CharSequence;IIZ)Ljava/util/List; + public static final fun windowed (Ljava/lang/CharSequence;IIZLkotlin/jvm/functions/Function1;)Ljava/util/List; + public static synthetic fun windowed$default (Ljava/lang/CharSequence;IIZILjava/lang/Object;)Ljava/util/List; + public static synthetic fun windowed$default (Ljava/lang/CharSequence;IIZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/util/List; + public static final fun windowedSequence (Ljava/lang/CharSequence;IIZ)Lkotlin/sequences/Sequence; + public static final fun windowedSequence (Ljava/lang/CharSequence;IIZLkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static synthetic fun windowedSequence$default (Ljava/lang/CharSequence;IIZILjava/lang/Object;)Lkotlin/sequences/Sequence; + public static synthetic fun windowedSequence$default (Ljava/lang/CharSequence;IIZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/sequences/Sequence; public static final fun withIndex (Ljava/lang/CharSequence;)Ljava/lang/Iterable; public static final fun zip (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/util/List; public static final fun zip (Ljava/lang/CharSequence;Ljava/lang/CharSequence;Lkotlin/jvm/functions/Function2;)Ljava/util/List; diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt index fa66f3af879..c1798949b3a 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt @@ -559,7 +559,7 @@ fun generators(): List { } } - templates add f("windowed(size: Int, step: Int, transform: (List) -> R)") { + templates add f("windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (List) -> R)") { since("1.2") only(Iterables, Sequences, CharSequences) doc { f -> @@ -574,7 +574,9 @@ fun generators(): List { Both [size] and [step] must be positive and can be greater than the number of elements in this ${f.collection}. @param size the number of elements to take in each window - @param step the number of elements to move the window forward by on an each step + @param step the number of elements to move the window forward by on an each step, by default 1 + @param partialWindows controls whether or not to keep partial windows in the end if any, + by default `false` which means partial windows won't be preserved @sample samples.collections.Sequences.Transformations.averageWindows """ @@ -593,20 +595,21 @@ fun generators(): List { var index = 0 while (index < thisSize) { window.move(index, (index + size).coerceAtMost(thisSize)) + if (!partialWindows && window.size < size) break result.add(transform(window)) index += step } return result } val result = ArrayList() - windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = true).forEach { + windowedIterator(iterator(), size, step, partialWindows, reuseBuffer = true).forEach { result.add(transform(it)) } return result """ } - customSignature(CharSequences) { "windowed(size: Int, step: Int, transform: (CharSequence) -> R)" } + customSignature(CharSequences) { "windowed(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R)" } body(CharSequences) { """ checkWindowSizeStep(size, step) @@ -614,7 +617,9 @@ fun generators(): List { val result = ArrayList((thisSize + step - 1) / step) var index = 0 while (index < thisSize) { - result.add(transform(subSequence(index, (index + size).coerceAtMost(thisSize)))) + val end = index + size + val coercedEnd = if (end > thisSize) { if (partialWindows) thisSize else break } else end + result.add(transform(subSequence(index, coercedEnd))) index += step } return result @@ -624,12 +629,12 @@ fun generators(): List { returns(Sequences) { "Sequence" } body(Sequences) { """ - return windowedSequence(size, step, dropTrailing = false, reuseBuffer = true).map(transform) + return windowedSequence(size, step, partialWindows, reuseBuffer = true).map(transform) """ } } - templates add f("windowed(size: Int, step: Int)") { + templates add f("windowed(size: Int, step: Int = 1, partialWindows: Boolean = false)") { since("1.2") only(Iterables, Sequences, CharSequences) returns(Iterables) { "List>" } @@ -646,7 +651,9 @@ fun generators(): List { Both [size] and [step] must be positive and can be greater than the number of elements in this ${f.collection}. @param size the number of elements to take in each window - @param step the number of elements to move the window forward by on an each step + @param step the number of elements to move the window forward by on an each step, by default 1 + @param partialWindows controls whether or not to keep partial windows in the end if any, + by default `false` which means partial windows won't be preserved @sample samples.collections.Sequences.Transformations.takeWindows """ @@ -660,27 +667,29 @@ fun generators(): List { val result = ArrayList>((thisSize + step - 1) / step) var index = 0 while (index < thisSize) { - result.add(List(size.coerceAtMost(thisSize - index)) { this[it + index] }) + val windowSize = size.coerceAtMost(thisSize - index) + if (windowSize < size && !partialWindows) break + result.add(List(windowSize) { this[it + index] }) index += step } return result } val result = ArrayList>() - windowedIterator(iterator(), size, step, dropTrailing = false, reuseBuffer = false).forEach { + windowedIterator(iterator(), size, step, partialWindows, reuseBuffer = false).forEach { result.add(it) } return result """ } - body(CharSequences) { "return windowed(size, step) { it.toString() }" } + body(CharSequences) { "return windowed(size, step, partialWindows) { it.toString() }" } body(Sequences) { """ - return windowedSequence(size, step, dropTrailing = false, reuseBuffer = false) + return windowedSequence(size, step, partialWindows, reuseBuffer = false) """ } } - templates add f("windowedSequence(size: Int, step: Int, transform: (CharSequence) -> R)") { + templates add f("windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false, transform: (CharSequence) -> R)") { since("1.2") only(CharSequences) doc { f -> @@ -695,7 +704,9 @@ fun generators(): List { Both [size] and [step] must be positive and can be greater than the number of elements in this ${f.collection}. @param size the number of elements to take in each window - @param step the number of elements to move the window forward by on an each step + @param step the number of elements to move the window forward by on an each step, by default 1 + @param partialWindows controls whether or not to keep partial windows in the end if any, + by default `false` which means partial windows won't be preserved @sample samples.collections.Sequences.Transformations.averageWindows """ @@ -706,12 +717,13 @@ fun generators(): List { body { """ checkWindowSizeStep(size, step) - return (indices step step).asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) } + val windows = (if (partialWindows) indices else 0 until length - size + 1) step step + return windows.asSequence().map { index -> transform(subSequence(index, (index + size).coerceAtMost(length))) } """ } } - templates add f("windowedSequence(size: Int, step: Int)") { + templates add f("windowedSequence(size: Int, step: Int = 1, partialWindows: Boolean = false)") { since("1.2") only(CharSequences) doc { f -> @@ -724,14 +736,16 @@ fun generators(): List { Both [size] and [step] must be positive and can be greater than the number of elements in this ${f.collection}. @param size the number of elements to take in each window - @param step the number of elements to move the window forward by on an each step + @param step the number of elements to move the window forward by on an each step, by default 1 + @param partialWindows controls whether or not to keep partial windows in the end if any, + by default `false` which means partial windows won't be preserved @sample samples.collections.Sequences.Transformations.takeWindows """ } returns { "Sequence "} - body(CharSequences) { "return windowedSequence(size, step) { it.toString() }" } + body(CharSequences) { "return windowedSequence(size, step, partialWindows) { it.toString() }" } } templates add f("chunked(size: Int, transform: (List) -> R)") { @@ -761,7 +775,7 @@ fun generators(): List { sequenceClassification(intermediate, stateful) returns(Sequences) { "Sequence" } - body { "return windowed(size, size, transform)" } + body { "return windowed(size, size, partialWindows = true, transform = transform)" } } templates add f("chunked(size: Int)") { @@ -784,7 +798,7 @@ fun generators(): List { sequenceClassification(intermediate, stateful) - body { "return windowed(size, size)" } + body { "return windowed(size, size, partialWindows = true)" } } templates add f("chunkedSequence(size: Int, transform: (CharSequence) -> R)") { @@ -812,7 +826,7 @@ fun generators(): List { body { """ - return windowedSequence(size, size, transform) + return windowedSequence(size, size, partialWindows = true, transform = transform) """ } }