com.xenomachina.chain.Chain.kt Maven / Gradle / Ivy
// Copyright © 2017 Laurence Gonsalves
//
// This file is part of kessel, a library which can be found at
// http://github.com/xenomachina/kessel
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2.1 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
// for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see http://www.gnu.org/licenses/
package com.xenomachina.chain
import kotlin.coroutines.experimental.SequenceBuilder
import kotlin.coroutines.experimental.buildIterator
/**
* A `Chain` represents a sequence of values. Like `Sequence`, a `Chain` is lazy, but unlike `Sequence`, earlier
* points along the chain can be remembered, which is useful for backtracking. A `Chain` is immutable, modulo laziness.
*/
sealed class Chain : Iterable {
/**
* @property head The first element of the chain.
*/
class NonEmpty(val head: T, tailProvider: () -> Chain) : Chain() {
override fun isEmpty(): Boolean = false
override fun iterator(): Iterator = ChainIterator(this)
/**
* The remaining elements, or null if there are none.
*/
val tail by lazy(tailProvider)
operator fun component1(): T = head
operator fun component2(): Chain = tail
override fun map(f: (T) -> F): Chain.NonEmpty = NonEmpty(f(head)) { tail.map(f) }
}
object Empty : Chain() {
override fun isEmpty(): Boolean = true
override fun iterator(): Iterator = emptySequence().iterator()
override fun map(f: (Nothing) -> F): Empty = this
}
abstract fun map(f: (T) -> F): Chain
abstract fun isEmpty(): Boolean
abstract override operator fun iterator(): Iterator
}
operator fun Chain.plus(that: () -> Chain): Chain =
when (this) {
// These look the same, but they dispatch to the more specifically-typed variants.
is Chain.Empty -> this + that
is Chain.NonEmpty -> this + that
}
operator fun > Chain.Empty.plus(that: () -> R): R =
that()
operator fun Chain.NonEmpty.plus(that: () -> Chain): Chain.NonEmpty =
Chain.NonEmpty(head) { tail + that }
fun buildChain(builderAction: suspend SequenceBuilder.() -> Unit): Chain =
buildIterator(builderAction).asChain()
class ChainIterator internal constructor (chain: Chain) : Iterator {
// For some reason, Kotlin didn't like it when I just used "private set", so simulate it in this roundabout way...
private var chainVar = chain
internal val chain: Chain
get() = chainVar
override fun hasNext() = !chainVar.isEmpty()
override fun next() = chainVar.let { s ->
when (s) {
is Chain.NonEmpty -> s.head.also { chainVar = s.tail }
else -> throw NoSuchElementException()
}
}
}
/**
* Returns a chain of the specified elements.
*/
fun chainOf(head: T, vararg tail: T): Chain.NonEmpty = chainOf(head, tail, offset = 0)
/**
* Returns a chain of no elements.
*/
fun chainOf(): Chain.Empty = Chain.Empty
private fun chainOf(head: T, tail: Array, offset: Int): Chain.NonEmpty {
return Chain.NonEmpty(head) {
if (offset >= tail.size) Chain.Empty
else chainOf(tail[offset], tail, offset + 1)
}
}
/**
* Converts a `Sequence` into a `Chain`. Note that the resulting `Chain` will lazily iterate the `Sequence`.
*/
fun Sequence.asChain(): Chain = iterator().asChain()
/**
* Converts an `Iterator` into a `Chain`. Note that the resulting `Chain` will lazily iterate the `Iterator`.
*/
fun Iterator.asChain(): Chain =
when (this) {
is ChainIterator -> chain
else -> if (hasNext()) {
Chain.NonEmpty(next(), this::asChain)
} else {
Chain.Empty
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy