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

dev.marksman.enhancediterables.ImmutableFiniteIterable Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
package dev.marksman.enhancediterables;

import com.jnape.palatable.lambda.adt.coproduct.CoProduct2;
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.Fn2;
import com.jnape.palatable.lambda.functions.builtin.fn1.Inits;
import com.jnape.palatable.lambda.functions.builtin.fn1.Reverse;
import com.jnape.palatable.lambda.functions.builtin.fn1.Tails;
import com.jnape.palatable.lambda.functions.builtin.fn2.*;
import com.jnape.palatable.lambda.functions.builtin.fn3.ZipWith;
import com.jnape.palatable.lambda.monoid.builtin.Concat;

import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
import static dev.marksman.enhancediterables.EnhancedIterables.*;
import static dev.marksman.enhancediterables.Validation.validateDrop;
import static dev.marksman.enhancediterables.Validation.validateSlide;
import static java.util.Objects.requireNonNull;

/**
 * An {@code EnhancedIterable} that is both finite and safe from mutation.
 *
 * @param  the element type
 */
public interface ImmutableFiniteIterable extends ImmutableIterable, FiniteIterable {

    /**
     * Lazily appends an element to the end of this {@code ImmutableFiniteIterable}, yielding a new {@code ImmutableNonEmptyFiniteIterable}.
     *
     * @param element the element to append
     * @return an ImmutableNonEmptyFiniteIterable<A>
     */
    @Override
    default ImmutableNonEmptyFiniteIterable append(A element) {
        return immutableNonEmptyFiniteIterableOrThrow(Snoc.snoc(element, this));
    }

    /**
     * Lazily concatenates another {@code ImmutableFiniteIterable} to the end of this {@code ImmutableFiniteIterable},
     * yielding a new {@code ImmutableFiniteIterable}.
     *
     * @param other the other {@link ImmutableFiniteIterable}
     * @return an {@code ImmutableFiniteIterable}
     */
    default ImmutableFiniteIterable concat(ImmutableFiniteIterable other) {
        requireNonNull(other);
        return immutableFiniteIterable(Concat.concat(this, other));
    }

    /**
     * Lazily concatenates an {@code ImmutableNonEmptyFiniteIterable} to the end of this {@code ImmutableFiniteIterable},
     * yielding a new {@code ImmutableNonEmptyFiniteIterable}.
     *
     * @param other an {@link ImmutableNonEmptyFiniteIterable}
     * @return an {@code ImmutableNonEmptyFiniteIterable}
     */
    default ImmutableNonEmptyFiniteIterable concat(ImmutableNonEmptyFiniteIterable other) {
        requireNonNull(other);
        return immutableNonEmptyFiniteIterableOrThrow(Concat.concat(this, other));
    }

    /**
     * Returns the lazily computed cartesian product of this {@code ImmutableFiniteIterable} with another {@code ImmutableFiniteIterable}.
     *
     * @param other an {@code ImmutableFiniteIterable} of any type
     * @param    the type of the other {@code ImmutableFiniteIterable}
     * @return a {@code ImmutableFiniteIterable>}
     */
    default  ImmutableFiniteIterable> cross(ImmutableFiniteIterable other) {
        requireNonNull(other);
        return immutableFiniteIterable(CartesianProduct.cartesianProduct(this, other));
    }

    /**
     * Returns a new {@code ImmutableFiniteIterable} that drops the first {@code count} elements of this {@code ImmutableFiniteIterable}.
     *
     * @param count the number of elements to drop from this {@code ImmutableFiniteIterable}.
     *              Must be >= 0.
     *              May exceed size of this {@code ImmutableFiniteIterable}, in which case, the result will be an
     *              empty {@code ImmutableFiniteIterable}.
     * @return an {@code ImmutableFiniteIterable}
     */
    @Override
    default ImmutableFiniteIterable drop(int count) {
        validateDrop(count);
        return immutableFiniteIterable(Drop.drop(count, this));
    }

    /**
     * Returns a new {@code ImmutableFiniteIterable} that skips the first contiguous group of elements of this
     * {@code ImmutableFiniteIterable} that satisfy a predicate.
     * 

* Iteration begins at the first element for which the predicate evaluates to false. * * @param predicate a predicate; should be referentially transparent and not have side-effects * @return a ImmutableFiniteIterable<A> */ @Override default ImmutableFiniteIterable dropWhile(Fn1 predicate) { requireNonNull(predicate); return immutableFiniteIterable(DropWhile.dropWhile(predicate, this)); } /** * Returns a new {@code ImmutableFiniteIterable} that contains all elements of this {@code ImmutableFiniteIterable} * that satisfy a predicate. * * @param predicate a predicate; should be referentially transparent and not have side-effects * @return a ImmutableFiniteIterable<A> */ @Override default ImmutableFiniteIterable filter(Fn1 predicate) { requireNonNull(predicate); return immutableFiniteIterable(Filter.filter(predicate).apply(this)); } /** * Returns a new {@code ImmutableFiniteIterable} by applying a function to all elements of this {@code ImmutableFiniteIterable}. * * @param f a function from {@code A} to {@code B}. * This function should be referentially transparent and not perform side-effects. * It may be called zero or more times for each element. * @param the type returned by {@code f} * @return an ImmutableFiniteIterable<B> */ @Override default ImmutableFiniteIterable fmap(Fn1 f) { requireNonNull(f); return immutableFiniteIterable(Map.map(f, this)); } default NonEmptyIterable> inits() { return nonEmptyIterableOrThrow(Map.map(EnhancedIterables::immutableFiniteIterable, Inits.inits(this))); } /** * Returns a new {@code ImmutableFiniteIterable} with the provided separator value injected between each value of this * {@code ImmutableFiniteIterable}. *

* If this {@code ImmutableFiniteIterable} contains fewer than two elements, it is left untouched. * * @param separator the separator value * @return a ImmutableFiniteIterable<A> */ @Override default ImmutableFiniteIterable intersperse(A separator) { return immutableFiniteIterable(Intersperse.intersperse(separator, this)); } /** * Partitions this {@code ImmutableFiniteIterable} given a disjoint mapping function. * * @param function the mapping function * @param The output left Iterable element type, as well as the CoProduct2 A type * @param The output right Iterable element type, as well as the CoProduct2 B type * @return a Tuple2<ImmutableFiniteIterable<B>, ImmutableFiniteIterable<C>> */ @Override default Tuple2, ? extends ImmutableFiniteIterable> partition( Fn1> function) { requireNonNull(function); Tuple2, Iterable> partitionResult = Partition.partition(function, this); return tuple(immutableFiniteIterable(partitionResult._1()), immutableFiniteIterable(partitionResult._2())); } /** * Lazily prepends an element to the front of this {@code ImmutableFiniteIterable}, yielding a new {@code ImmutableNonEmptyFiniteIterable}. * * @param element the element to prepend * @return a ImmutableNonEmptyFiniteIterable<A> */ @Override default ImmutableNonEmptyFiniteIterable prepend(A element) { return ImmutableNonEmptyFiniteIterable.immutableNonEmptyFiniteIterable(element, this); } /** * Returns a new {@code ImmutableFiniteIterable} with the provided separator value injected before each value of this * {@code ImmutableFiniteIterable}. *

* If this {@code ImmutableFiniteIterable} is empty, it is left untouched. * * @param separator the separator value * @return a ImmutableFiniteIterable<A> */ @Override default ImmutableFiniteIterable prependAll(A separator) { return immutableFiniteIterable(PrependAll.prependAll(separator, this)); } @Override default ImmutableFiniteIterable reverse() { return immutableFiniteIterable(Reverse.reverse(this)); } /** * "Slide" a window of {@code k} elements across the {@code ImmutableFiniteIterable} by one element at a time. *

* Example: * * ImmutableFiniteIterable.of(1, 2, 3, 4, 5).slide(2); // [[1, 2], [2, 3], [3, 4], [4, 5]] * * @param k the number of elements in the sliding window. Must be >= 1. * @return an {@code ImmutableFiniteIterable>} */ @Override default ImmutableFiniteIterable> slide(int k) { validateSlide(k); return immutableFiniteIterable(Map.map(EnhancedIterables::immutableNonEmptyFiniteIterableOrThrow, Slide.slide(k, this))); } /** * Returns a {@code Tuple2} where the first slot is the front contiguous elements of this * {@code ImmutableFiniteIterable} matching a predicate and the second slot is all the remaining elements. * * @param predicate a predicate; should be referentially transparent and not have side-effects * @return a Tuple2<ImmutableFiniteIterable<B>, ImmutableFiniteIterable<C>> */ @Override default Tuple2, ? extends ImmutableFiniteIterable> span(Fn1 predicate) { requireNonNull(predicate); Tuple2, Iterable> spanResult = Span.span(predicate).apply(this); return tuple(immutableFiniteIterable(spanResult._1()), immutableFiniteIterable(spanResult._2())); } /** * Returns a {@code NonEmptyIterable} containing all of the subsequences of tail * elements of this {@code ImmutableFiniteIterable}, ordered by size, starting with the full list. * Example: * * ImmutableFiniteIterable.of(1, 2, 3).tails(); // [[1, 2, 3], [2, 3], [3], []] * * @return a {@code NonEmptyIterable>} */ @Override default NonEmptyIterable> tails() { return nonEmptyIterableOrThrow(Map.map(EnhancedIterables::immutableFiniteIterable, Tails.tails(this))); } @Override default ImmutableFiniteIterable takeWhile(Fn1 predicate) { requireNonNull(predicate); return immutableFiniteIterable(TakeWhile.takeWhile(predicate, this)); } default ImmutableFiniteIterable zipWith(Fn2 fn, ImmutableIterable other) { requireNonNull(fn); requireNonNull(other); return immutableFiniteIterable(ZipWith.zipWith(fn, this, other)); } @SafeVarargs static ImmutableNonEmptyFiniteIterable of(A first, A... more) { return EnhancedIterables.of(first, more); } }