dev.marksman.enhancediterables.NonEmptyFiniteIterable Maven / Gradle / Ivy
Show all versions of enhanced-iterables Show documentation
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 super A, ? extends B> 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 extends 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 super A, ? super A, ? extends A> 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 super A, ? super A, ? extends A> 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 extends NonEmptyFiniteIterable> 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 extends NonEmptyFiniteIterable> 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);
}
}
}