48 lines
1.5 KiB
Kotlin
48 lines
1.5 KiB
Kotlin
package moe.kageru.sekwences
|
|
|
|
/**
|
|
* Returns a sequences that optionally merges adjacent elements.
|
|
* Heavily inspired by Itertools.coalesce() from the Rust crate.
|
|
*
|
|
* https://docs.rs/itertools/0.8.0/itertools/trait.Itertools.html#method.coalesce
|
|
*/
|
|
fun <T> Sequence<T>.coalesce(merger: (T, T) -> T?): Sequence<T> {
|
|
return if (this.iterator().hasNext()) {
|
|
CoalescingSequence(this, merger)
|
|
} else {
|
|
emptySequence()
|
|
}
|
|
}
|
|
|
|
class CoalescingSequence<T>(private val source: Sequence<T>, private inline val merger: (T, T) -> T?) : Sequence<T> {
|
|
override fun iterator(): Iterator<T> {
|
|
return CoalescingIterator(source.iterator(), merger)
|
|
}
|
|
}
|
|
|
|
class CoalescingIterator<T>(private val source: Iterator<T>, private inline val merger: (T, T) -> T?) : Iterator<T> {
|
|
private var previous: T = if (source.hasNext()) source.next() else error("Please don’t pass empty iterators in here")
|
|
private var hasNext = true
|
|
|
|
override fun hasNext() = hasNext
|
|
|
|
override tailrec fun next(): T {
|
|
if (!source.hasNext()) {
|
|
hasNext = false
|
|
return previous
|
|
}
|
|
|
|
val current = source.next()
|
|
val merged = merger(previous, current)
|
|
// If the elements can’t be merged, return the first, then save the second for next time.
|
|
return if (merged == null) {
|
|
val ret = previous
|
|
previous = current
|
|
ret
|
|
} else {
|
|
previous = merged
|
|
next()
|
|
}
|
|
}
|
|
}
|