-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new Helper MixIn inTwoBlocks: process a sequence/iterator in two bloc…
…ks by a delimiter lambda, with one lambda consuming the first part of the iterable/sequence, and a second lambda that gets the result of the first lambda and the remaining iterable/sequence
- Loading branch information
Showing
2 changed files
with
129 additions
and
0 deletions.
There are no files selected for viewing
58 changes: 58 additions & 0 deletions
58
lib/src/main/kotlin/de/linkel/aoc/utils/iterables/TwoBlockSequenceMixIn.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package de.linkel.aoc.utils.iterables | ||
|
||
|
||
class StoppingIterator<T>( | ||
private val parent: Iterator<T>, | ||
private val endPredicate: (T) -> Boolean | ||
): Iterator<T> { | ||
private var buffer: T? = null | ||
private var consumed: Boolean = true | ||
|
||
init { | ||
if (parent.hasNext()) { | ||
buffer = parent.next() | ||
if (!endPredicate(buffer!!)) { | ||
consumed = false | ||
} | ||
} | ||
} | ||
|
||
override fun hasNext(): Boolean { | ||
return !consumed && !endPredicate(buffer!!) | ||
} | ||
|
||
override fun next(): T { | ||
if (hasNext()) { | ||
val result = buffer!! | ||
if (parent.hasNext()) { | ||
buffer = parent.next() | ||
} else { | ||
consumed = true | ||
} | ||
return result | ||
} else throw NoSuchElementException() | ||
} | ||
} | ||
class IteratorSequence<T>( | ||
private val iterator: Iterator<T> | ||
): Sequence<T> { | ||
private var consumed = false | ||
override fun iterator(): Iterator<T> { | ||
if (consumed) throw NoSuchElementException() | ||
consumed = true | ||
return iterator | ||
} | ||
} | ||
|
||
fun <A,B,T> Sequence<T>.inTwoBlocks(delimiterPredicate: (T) -> Boolean, block1: (Sequence<T>) -> A, block2: (buffer: A, Sequence<T>) -> B): B { | ||
val iterator = this.iterator() | ||
|
||
val first = block1(IteratorSequence(StoppingIterator(iterator, delimiterPredicate))) | ||
return block2(first, IteratorSequence(iterator)) | ||
} | ||
fun <A,B,T> Iterable<T>.inTwoBlocks(delimiterPredicate: (T) -> Boolean, block1: (Sequence<T>) -> A, block2: (buffer: A, Sequence<T>) -> B): B { | ||
val iterator = this.iterator() | ||
|
||
val first = block1(IteratorSequence(StoppingIterator(iterator, delimiterPredicate))) | ||
return block2(first, IteratorSequence(iterator)) | ||
} |
71 changes: 71 additions & 0 deletions
71
lib/src/test/kotlin/de/linkel/aoc/utils/iterables/TwoBlockSequenceTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package de.linkel.aoc.utils.iterables | ||
|
||
import org.assertj.core.api.Assertions | ||
import org.junit.jupiter.api.Test | ||
|
||
class TwoBlockSequenceTest { | ||
@Test | ||
fun `empty sequence calls both blocks with empty sequence`() { | ||
Assertions.assertThat( | ||
emptySequence<String>() | ||
.inTwoBlocks(String::isEmpty, | ||
{ seq -> seq.count() }, | ||
{ a, seq -> a to seq.count() } | ||
) | ||
).isEqualTo(0 to 0) | ||
} | ||
|
||
@Test | ||
fun `sequence of lines can be split on empty line`() { | ||
Assertions.assertThat( | ||
""" | ||
erste zeile | ||
zweite zeile | ||
dritte zeile | ||
vierte zeile | ||
""".trimIndent() | ||
.lineSequence() | ||
.inTwoBlocks(String::isEmpty, | ||
{ seq -> seq.toList() }, | ||
{ a, seq -> listOf(a, seq.toList()) } | ||
) | ||
).isEqualTo(listOf( | ||
listOf("erste zeile", "zweite zeile"), | ||
listOf("dritte zeile", "vierte zeile") | ||
)) | ||
} | ||
|
||
@Test | ||
fun `empty iterable calls both blocks with empty sequence`() { | ||
Assertions.assertThat( | ||
emptyList<String>() | ||
.inTwoBlocks(String::isEmpty, | ||
{ seq -> seq.count() }, | ||
{ a, seq -> a to seq.count() } | ||
) | ||
).isEqualTo(0 to 0) | ||
} | ||
|
||
@Test | ||
fun `iterable of lines can be split on empty line`() { | ||
Assertions.assertThat( | ||
""" | ||
erste zeile | ||
zweite zeile | ||
dritte zeile | ||
vierte zeile | ||
""".trimIndent() | ||
.lineSequence() | ||
.toList() | ||
.inTwoBlocks(String::isEmpty, | ||
{ seq -> seq.toList() }, | ||
{ a, seq -> listOf(a, seq.toList()) } | ||
) | ||
).isEqualTo(listOf( | ||
listOf("erste zeile", "zweite zeile"), | ||
listOf("dritte zeile", "vierte zeile") | ||
)) | ||
} | ||
} |