All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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