dev.marksman.enhancediterables.FiniteIterable 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.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 super A, ? extends Boolean> 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 super A, ? extends Boolean> 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 super A, ? extends B> 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 super B, ? super A, ? extends B> 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 super A, ? super Lazy, ? 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 extends FiniteIterable> 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 extends NonEmptyFiniteIterable> 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, ? extends FiniteIterable> partition(
Fn1 super A, ? extends CoProduct2> 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 extends NonEmptyFiniteIterable> 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, ? extends FiniteIterable> span(Fn1 super A, ? extends Boolean> 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 extends FiniteIterable> 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 super A, ? extends Boolean> 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 extends FiniteIterable> 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 extends NonEmptyFiniteIterable> 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);
}
}
}