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

dev.marksman.enhancediterables.NonEmptyFiniteIterable 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.hlist.Tuple2;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.Fn2;
import com.jnape.palatable.lambda.functions.builtin.fn1.Init;
import com.jnape.palatable.lambda.functions.builtin.fn1.Last;
import com.jnape.palatable.lambda.functions.builtin.fn1.Reverse;
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 java.util.Collection;

import static com.jnape.palatable.lambda.adt.Maybe.just;
import static dev.marksman.enhancediterables.EnhancedIterables.nonEmptyFiniteIterableOrThrow;
import static java.util.Objects.requireNonNull;

/**
 * An {@code EnhancedIterable} that is finite and guaranteed to contain at least one element.
 *
 * @param  the element type
 */
public interface NonEmptyFiniteIterable extends FiniteIterable, NonEmptyIterable {

    /**
     * Returns an {@code FiniteIterable} containing all subsequent elements of this one beyond the first.
     *
     * @return an {@code FiniteIterable}
     */
    @Override
    FiniteIterable tail();

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

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

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

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

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

    /**
     * Returns a new {@code NonEmptyFiniteIterable} by applying a function to all elements of this {@code NonEmptyFiniteIterable}.
     *
     * @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 NonEmptyFiniteIterable}
     */
    @Override
    default  NonEmptyFiniteIterable fmap(Fn1 f) {
        requireNonNull(f);
        return nonEmptyFiniteIterableOrThrow(Map.map(f, this));
    }

    /**
     * Returns a {@code FiniteIterable} containing all of elements of this one, except for the last element.
     *
     * @return a {@code FiniteIterable}
     */
    default FiniteIterable init() {
        return EnhancedIterables.finiteIterable(Init.init(this));
    }

    /**
     * Returns a new {@code NonEmptyFiniteIterable} with the provided separator value injected between each value of this
     * {@code NonEmptyFiniteIterable}.
     * 

* If this {@code NonEmptyFiniteIterable} contains only one element, it is left untouched. * * @param separator the separator value * @return a {@code NonEmptyFiniteIterable} */ @Override default NonEmptyFiniteIterable intersperse(A separator) { return nonEmptyFiniteIterableOrThrow(Intersperse.intersperse(separator, this)); } /** * Returns the last element. * * @return an element of type {@code A} */ default A last() { return Last.last(this).orElseThrow(AssertionError::new); } /** * Returns an {@code Iterable} of contiguous groups of elements in this {@code NonEmptyFiniteIterable} 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 an {@code NonEmptyFiniteIterable>} containing the contiguous groups */ @Override default NonEmptyFiniteIterable> magnetizeBy(Fn2 predicate) { requireNonNull(predicate); return nonEmptyFiniteIterableOrThrow(MagnetizeBy.magnetizeBy(predicate, this)) .fmap(EnhancedIterables::nonEmptyFiniteIterableOrThrow); } /** * Returns a new {@code NonEmptyFiniteIterable} with the provided separator value injected before each value of this * {@code NonEmptyFiniteIterable}. * * @param separator the separator value * @return a {@code NonEmptyFiniteIterable} */ @Override default NonEmptyFiniteIterable prependAll(A separator) { return nonEmptyFiniteIterableOrThrow(PrependAll.prependAll(separator, this)); } /** * Applies a binary operator to all elements of this {@code NonEmptyFiniteIterable}, going left to right. * * @param op the binary operator (accumulator on the left, item on the right) * @return the result of inserting {@code op} between consecutive elements of this {@code NonEmptyFiniteIterable}, * going left to right: * * op( op( ... op(x_1, x_2) ..., x_{n-1}), x_n) * * where x,,1,,, ..., x,,n,, are the elements of this {@code NonEmptyFiniteIterable} */ default A reduceLeft(Fn2 op) { return tail().foldLeft(op, head()); } /** * Applies a binary operator to all elements of this {@code NonEmptyFiniteIterable}, going right to left. * * @param op the binary operator (item on the left, accumulator on the right) * @return the result of inserting {@code op} between consecutive elements of this {@code NonEmptyFiniteIterable}, * going right to left: * * op(x_1, op(x_2, ..., op(x_{n-1}, x_n)...)) * * where x,,1,,, ..., x,,n,, are the elements of this {@code NonEmptyFiniteIterable} */ default A reduceRight(Fn2 op) { return reverse().reduceLeft(op.flip()); } /** * Returns a reversed representation of this {@code NonEmptyFiniteIterable}. *

* Note that reversing is deferred until the returned {@code Iterable} is iterated. * * @return a {@code NonEmptyFiniteIterable} */ @Override default NonEmptyFiniteIterable reverse() { return nonEmptyFiniteIterableOrThrow(Reverse.reverse(this)); } /** * Always succeeds because {@code NonEmptyFiniteIterable}s are always finite. * * @return this {@code NonEmptyFiniteIterable} wrapped in a `just` */ @Override default Maybe> toFinite() { return just(this); } /** * Always succeeds because {@code NonEmptyFiniteIterable}s are always non-empty. * * @return this {@code NonEmptyFiniteIterable} wrapped in a `just` */ @Override default Maybe> toNonEmpty() { return just(this); } /** * Zips together this {@code NonEmptyFiniteIterable} with an {@code NonEmptyIterable} 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 NonEmptyFiniteIterable} */ default NonEmptyFiniteIterable zipWith(Fn2 fn, NonEmptyIterable other) { requireNonNull(fn); requireNonNull(other); return nonEmptyFiniteIterableOrThrow(ZipWith.zipWith(fn, this, other)); } /** * Creates a {@code NonEmptyFiniteIterable}. * * @param head the first element * @param tail the remaining elements. May be empty. * @param the element type * @return a {@code NonEmptyFiniteIterable} */ static NonEmptyFiniteIterable nonEmptyFiniteIterable(A head, FiniteIterable tail) { return EnhancedIterables.nonEmptyFiniteIterable(head, tail); } /** * Creates a {@code NonEmptyFiniteIterable}. * * @param head the first element * @param tail the remaining elements. May be empty. * @param the element type * @return a {@code NonEmptyFiniteIterable} */ static NonEmptyFiniteIterable nonEmptyFiniteIterable(A head, Collection tail) { return EnhancedIterables.nonEmptyFiniteIterable(head, tail); } /** * Creates a {@code NonEmptyFiniteIterable} containing the given elements. *

* Note that this method actually returns an {@link ImmutableNonEmptyFiniteIterable}, which is * also an {@link NonEmptyFiniteIterable}. * * @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); } } }