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

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

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

import com.jnape.palatable.lambda.adt.Maybe;
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.FoldLeft;
import com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight;
import com.jnape.palatable.lambda.functions.builtin.fn3.ZipWith;
import com.jnape.palatable.lambda.functor.builtin.Lazy;
import com.jnape.palatable.lambda.monoid.builtin.Concat;

import java.util.Collection;

import static com.jnape.palatable.lambda.adt.Maybe.just;
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
import static dev.marksman.enhancediterables.EnhancedIterable.enhance;
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 finite.
 *
 * @param  the element type
 */
public interface FiniteIterable extends EnhancedIterable {

    /**
     * Lazily appends an element to the end of this {@code FiniteIterable}, yielding a new {@code NonEmptyFiniteIterable}.
     *
     * @param element the element to append
     * @return a {@code NonEmptyFiniteIterable}
     */
    @Override
    default NonEmptyFiniteIterable append(A element) {
        return nonEmptyFiniteIterableOrThrow(Snoc.snoc(element, this));
    }

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

    /**
     * Lazily concatenates a {@code Collection} to the end of this {@code FiniteIterable},
     * yielding a new {@code FiniteIterable}.
     *
     * @param other a {@link Collection}
     * @return a {@code FiniteIterable}
     */
    default FiniteIterable concat(Collection other) {
        requireNonNull(other);
        return EnhancedIterables.finiteIterable(Concat.concat(this, other));
    }

    /**
     * Lazily concatenates a {@code NonEmptyFiniteIterable} to the end of this {@code FiniteIterable},
     * yielding a new {@code NonEmptyFiniteIterable}.
     *
     * @param other a {@link NonEmptyFiniteIterable}
     * @return a {@code NonEmptyFiniteIterable}
     */
    default NonEmptyFiniteIterable concat(NonEmptyFiniteIterable other) {
        requireNonNull(other);
        return EnhancedIterables.nonEmptyFiniteIterableOrThrow(Concat.concat(this, other));
    }

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

    /**
     * Returns the lazily computed cartesian product of this {@code FiniteIterable} with a {@code Collection}.
     *
     * @param other a {@link Collection} of any type
     * @param    the type of the other {@code Collection}
     * @return a {@code FiniteIterable>}
     */
    default  FiniteIterable> cross(Collection other) {
        requireNonNull(other);
        return EnhancedIterables.finiteIterable(CartesianProduct.cartesianProduct(this, other));
    }

    /**
     * Returns an infinite {@code EnhancedIterable} that repeatedly cycles this {@code FiniteIterable}'s elements,
     * in order.
     *
     * @return an {@code EnhancedIterable}
     */
    default EnhancedIterable cycle() {
        return EnhancedIterables.cycle(this);
    }

    /**
     * Returns a {@code FiniteIterable} of the distinct values from this {@link FiniteIterable}.
     *
     * @return a {@code FiniteIterable}
     */
    default FiniteIterable distinct() {
        return EnhancedIterables.distinct(this);
    }

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

    /**
     * Returns a new {@code FiniteIterable} that skips the first contiguous group of elements of this
     * {@code FiniteIterable} 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 {@code FiniteIterable} */ @Override default FiniteIterable dropWhile(Fn1 predicate) { requireNonNull(predicate); return EnhancedIterables.finiteIterable(DropWhile.dropWhile(predicate, this)); } /** * Returns a new {@code FiniteIterable} that contains all elements of this {@code FiniteIterable} * that satisfy a predicate. * * @param predicate a predicate; should be referentially transparent and not have side-effects * @return a {@code FiniteIterable} */ @Override default FiniteIterable filter(Fn1 predicate) { requireNonNull(predicate); return EnhancedIterables.finiteIterable(Filter.filter(predicate).apply(this)); } /** * Returns a new {@code FiniteIterable} by applying a function to all elements of this {@code FiniteIterable}. * * @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 a {@code FiniteIterableonEmptyFiniteIterable} */ @Override default FiniteIterable fmap(Fn1 f) { requireNonNull(f); return EnhancedIterables.finiteIterable(Map.map(f, this)); } /** * Applies a binary operator to a start value and all elements of this {@code FiniteIterable}, going left to right. * * @param z the start value * @param op the binary operator * @param the result type of the binary operator * @return the result of inserting {@code op} between consecutive elements of this {@code FiniteIterable}, * going left to right with the start value {@code z} on the left: * * op(...op(z, x_1), x_2, ..., x_n) * * where x,,1,,, ..., x,,n,, are the elements of this {@code FiniteIterable} * Returns {@code z} if this {@code FiniteIterable} is empty. */ default B foldLeft(Fn2 op, B z) { requireNonNull(op); return FoldLeft.foldLeft(op, z).apply(this); } /** * Applies a binary operator to a start value and all elements of this {@code FiniteIterable}, going right to left. *

* This method is computationally the iterative inverse of {@link FiniteIterable#foldLeft}, but uses {@code Lazy} to support stack-safe * execution. * * @param z the start value * @param op the binary operator * @param the result type of the binary operator * @return a {@code Lazy} that evaluates to the result of inserting {@code op} between consecutive elements of * this {@code FiniteIterable}, going right to left with the start value {@code z} on the right: * * op(x_1, op(x_2, ... op(x_n, z)...)) * * where x,,1,,, ..., x,,n,, are the elements of this {@code FiniteIterable} * Returns {@code z} if this {@code FiniteIterable} is empty. */ default Lazy foldRight(Fn2, ? extends Lazy> op, Lazy z) { requireNonNull(op); return FoldRight.foldRight(op, z).apply(this); } /** * Returns a {@code ImmutableNonEmptyIterable} containing all of the subsequences of initial * elements of this {@code FiniteIterable}, ordered by size, starting with the empty list. * Example: * * FiniteIterable.of(1, 2, 3).inits(); // [[], [1], [1, 2], [1, 2, 3]] * * @return a {@code ImmutableNonEmptyFiniteIterable>} */ default ImmutableNonEmptyFiniteIterable> inits() { return immutableNonEmptyFiniteIterableOrThrow(Map.map(EnhancedIterables::finiteIterable, Inits.inits(this))); } /** * Returns a new {@code FiniteIterable} with the provided separator value injected between each value of this * {@code FiniteIterable}. *

* If this {@code FiniteIterable} contains fewer than two elements, it is left untouched. * * @param separator the separator value * @return a {@code FiniteIterable} */ @Override default FiniteIterable intersperse(A separator) { return EnhancedIterables.finiteIterable(Intersperse.intersperse(separator, this)); } /** * Returns an {@code Iterable} of contiguous groups of elements in this {@code FiniteIterable} that match a * predicate pairwise. * * @param predicate the predicate function. * This function should be referentially transparent and not perform side-effects. * It may be called zero or more times for each element. * @return a {@code FiniteIterable>} containing the contiguous groups */ @Override default FiniteIterable> magnetizeBy(Fn2 predicate) { requireNonNull(predicate); return EnhancedIterables.finiteIterable(MagnetizeBy.magnetizeBy(predicate, this)) .fmap(EnhancedIterables::nonEmptyFiniteIterableOrThrow); } /** * Partitions this {@code FiniteIterable} 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 {@code Tuple2} */ @Override default Tuple2, ? extends FiniteIterable> partition( Fn1> function) { requireNonNull(function); Tuple2, Iterable> partitionResult = Partition.partition(function, this); return tuple(EnhancedIterables.finiteIterable(partitionResult._1()), EnhancedIterables.finiteIterable(partitionResult._2())); } /** * Lazily prepends an element to the front of this {@code FiniteIterable}, yielding a new {@code NonEmptyFiniteIterable}. * * @param element the element to prepend * @return a {@code NonEmptyFiniteIterable} */ @Override default NonEmptyFiniteIterable prepend(A element) { return NonEmptyFiniteIterable.nonEmptyFiniteIterable(element, this); } /** * Returns a new {@code FiniteIterable} with the provided separator value injected before each value of this * {@code FiniteIterable}. *

* If this {@code FiniteIterable} is empty, it is left untouched. * * @param separator the separator value * @return a {@code FiniteIterable} */ @Override default FiniteIterable prependAll(A separator) { return EnhancedIterables.finiteIterable(PrependAll.prependAll(separator, this)); } /** * Returns a reversed representation of this {@code FiniteIterable}. *

* Note that reversing is deferred until the returned {@code Iterable} is iterated. * * @return a {@code FiniteIterable} */ default FiniteIterable reverse() { return EnhancedIterables.finiteIterable(Reverse.reverse(this)); } /** * Returns the number of elements in this {@code FiniteIterable}. *

* If this {@code FiniteIterable} contains more than Integer.MAX_VALUE elements, returns * Integer.MAX_VALUE. * * @return the number of elements in this {@code FiniteIterable} */ default int size() { return EnhancedIterables.size(this); } /** * "Slides" a window of {@code k} elements across the {@code FiniteIterable} by one element at a time. *

* Example: * * FiniteIterable.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 a {@code FiniteIterable>} */ @Override default FiniteIterable> slide(int k) { validateSlide(k); return EnhancedIterables.finiteIterable(Map.map(EnhancedIterables::nonEmptyFiniteIterableOrThrow, Slide.slide(k, this))); } /** * Returns a {@code Tuple2} where the first slot is the front contiguous elements of this * {@code FiniteIterable} 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 {@code Tuple2} */ @Override default Tuple2, ? extends FiniteIterable> span(Fn1 predicate) { requireNonNull(predicate); Tuple2, Iterable> spanResult = Span.span(predicate).apply(this); return tuple(EnhancedIterables.finiteIterable(spanResult._1()), EnhancedIterables.finiteIterable(spanResult._2())); } /** * Returns an {@code ImmutableNonEmptyIterable} containing all of the subsequences of tail * elements of this {@code FiniteIterable}, ordered by size, starting with the full list. * Example: * * FiniteIterable.of(1, 2, 3).tails(); // [[1, 2, 3], [2, 3], [3], []] * * @return an {@code ImmutableNonEmptyIterable>} */ @Override default ImmutableNonEmptyIterable> tails() { return immutableNonEmptyIterableOrThrow(Map.map(EnhancedIterables::finiteIterable, Tails.tails(this))); } /** * Returns a new {@code FiniteIterable} that limits to the first contiguous group of elements of this * {@code FiniteIterable} that satisfy a predicate. *

* Iteration ends at, but does not include, 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 {@code FiniteIterable} */ @Override default FiniteIterable takeWhile(Fn1 predicate) { requireNonNull(predicate); return EnhancedIterables.finiteIterable(TakeWhile.takeWhile(predicate, this)); } /** * Always succeeds because {@code FiniteIterable}s are always finite. * * @return this {@code FiniteIterable} wrapped in a `just` */ @Override default Maybe> toFinite() { return just(this); } /** * Converts this {@code FiniteIterable} to a {@code NonEmptyFiniteIterable} if it contains * one or more elements. * * @return a {@code Maybe} */ @Override default Maybe> toNonEmpty() { return EnhancedIterables.maybeNonEmpty(this) .fmap(EnhancedIterables::nonEmptyFiniteIterableOrThrow); } /** * Zips together this {@code FiniteIterable} with another {@code Iterable} by applying a zipping function. *

* Applies the function to the successive elements of each {@code Iterable} until one of them runs out of elements. * * @param fn the zipping function. * Not null. * This function should be referentially transparent and not perform side-effects. * It may be called zero or more times for each element. * @param other the other {@code Iterable} * @param the element type of the other {@code Iterable} * @param the element type of the result * @return an {@code FiniteIterable} */ default FiniteIterable zipWith(Fn2 fn, Iterable other) { requireNonNull(fn); requireNonNull(other); return EnhancedIterables.finiteIterable(ZipWith.zipWith(fn, this, other)); } /** * Creates an {@code ImmutableFiniteIterable} by copying elements from a {@code FiniteIterable}. *

* If {@code source} is already an {@code ImmutableFiniteIterable}, this method will return it without copying. * * @param source the source to copy from * @param the element type * @return an {@code ImmutableFiniteIterable} */ static ImmutableFiniteIterable copyFrom(FiniteIterable source) { return EnhancedIterables.copyFrom(source); } /** * Creates an {@code ImmutableFiniteIterable} by copying elements from a {@code Collection}. * * @param source the source to copy from * @param the element type * @return an {@code ImmutableFiniteIterable} */ static ImmutableFiniteIterable copyFrom(Collection source) { return EnhancedIterables.copyFrom(source); } /** * Creates an {@code ImmutableFiniteIterable} by copying elements from an {@code Iterable}. *

* If {@code source} is already an {@code ImmutableIterable}, no copying will be performed. * * @param maxCount the maximum number of elements to take from the supplied {@link Iterable}. * Must be >= 0. * May exceed size of the {@code Iterable}, in which case, the result will contain * as many elements available. * @param source the source to copy from * @param the element type * @return an {@code ImmutableFiniteIterable} */ static ImmutableFiniteIterable copyFrom(int maxCount, Iterable source) { return EnhancedIterables.copyFrom(maxCount, source); } /** * Creates an empty {@code FiniteIterable}. * * @param the element type * @return an {@code ImmutableFiniteIterable} */ static ImmutableFiniteIterable emptyFiniteIterable() { return EnhancedIterables.emptyEnhancedIterable(); } /** * Creates a {@code FiniteIterable} by wrapping a {@code Collection}. *

* Does not make a copy of the {@link Collection}. * * @param collection the source {@code Collection} * @param the element type * @return a {@code FiniteIterable} */ static FiniteIterable finiteIterable(Collection collection) { requireNonNull(collection); return EnhancedIterables.finiteIterable(collection); } /** * Creates a {@code FiniteIterable} by wrapping an {@code Iterable}. * * @param maxCount the maximum number of elements to take from the supplied {@link Iterable}. * Must be >= 0. * May exceed size of the {@code Iterable}, in which case, the result will contain * as many elements available. * @param iterable the source {@code Iterable} * @param the element type * @return a {@code FiniteIterable} */ static FiniteIterable finiteIterable(int maxCount, Iterable iterable) { return enhance(iterable).take(maxCount); } /** * Creates a {@code FiniteIterable} containing the given elements. *

* Note that this method actually returns an {@link ImmutableNonEmptyFiniteIterable}, which is * also a {@link FiniteIterable}. * * @param first the first element * @param more the remaining elements * @param the element type * @return an {@code ImmutableNonEmptyFiniteIterable} */ @SuppressWarnings("varargs") @SafeVarargs static ImmutableNonEmptyFiniteIterable of(A first, A... more) { if (more.length > 0) { return EnhancedIterables.of(first, more); } else { return EnhancedIterables.singleton(first); } } }