2019-10-07 23:19:48 +02:00
package moe.kageru.sekwences
/ * *
* Returns a sequences that optionally merges adjacent elements .
* Heavily inspired by Itertools . coalesce ( ) from the Rust crate .
*
2019-10-07 23:25:23 +02:00
* The merge function will be passed two adjacent elements in the sequence .
* If the two can be merged , the result of the merging process should be returned .
* If null is returned , the first of the two elements is returned ,
* and the second will be used as the first element in the next merge operation .
*
2019-10-07 23:19:48 +02:00
* 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 ( )
}
}
2019-10-07 23:25:23 +02:00
/ * *
* Sequence that holds a [ CoalescingIterator ] .
* /
internal class CoalescingSequence < T > ( private val source : Sequence < T > , private inline val merger : ( T , T ) -> T ? ) : Sequence < T > {
2019-10-07 23:19:48 +02:00
override fun iterator ( ) : Iterator < T > {
return CoalescingIterator ( source . iterator ( ) , merger )
}
}
2019-10-07 23:25:23 +02:00
/ * *
* Iterator that will attempt to merge adjacent elements as described in the KDoc for [ coalesce ] .
* /
internal class CoalescingIterator < T > ( private val source : Iterator < T > , private inline val merger : ( T , T ) -> T ? ) : Iterator < T > {
2019-10-07 23:19:48 +02:00
private var previous : T = if ( source . hasNext ( ) ) source . next ( ) else error ( " Please don’t pass empty iterators in here " )
2019-10-07 23:25:23 +02:00
// The reason we need this marker is that our iterator can still hold one value, even if the source has been drained.
2019-10-07 23:19:48 +02:00
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 ( )
}
}
}