
fj.data.List Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionaljava Show documentation
Show all versions of functionaljava Show documentation
Functional Java is an open source library that supports closures for the Java programming language
The newest version!
package fj.data;
import fj.*;
import fj.control.Trampoline;
import fj.control.parallel.*;
import fj.data.optic.Optional;
import fj.data.optic.*;
import fj.data.vector.V2;
import fj.function.Effect1;
import java.lang.Class;
import java.util.*;
import static fj.Bottom.error;
import static fj.Function.*;
import static fj.Ord.intOrd;
import static fj.Ordering.GT;
import static fj.P.*;
import static fj.Unit.unit;
import static fj.data.Array.mkArray;
import static fj.data.Either.*;
import static fj.data.List.Buffer.*;
import static fj.data.Option.*;
import static fj.data.optic.Optional.optional;
import static fj.data.optic.Prism.prism;
import static fj.data.vector.V.v;
import static fj.function.Booleans.not;
/**
* Provides an in-memory, immutable, singly linked list.
*/
public abstract class List implements Iterable {
private List() {
}
/**
* Returns an iterator for this list. This method exists to permit the use in a for
-each loop.
*
* @return A iterator for this list.
*/
public final Iterator iterator() {
return toCollection().iterator();
}
/**
* The first element of the linked list or fails for the empty list.
*
* @return The first element of the linked list or fails for the empty list.
*/
public abstract A head();
/**
* The list without the first element or fails for the empty list.
*
* @return The list without the first element or fails for the empty list.
*/
public abstract List tail();
/**
* The length of this list.
*
* @return The length of this list.
*/
public final int length() {
return foldLeft((i, a) -> i + 1, 0);
}
/**
* Returns true
if this list is empty, false
otherwise.
*
* @return true
if this list is empty, false
otherwise.
*/
public final boolean isEmpty() {
return this instanceof Nil;
}
/**
* Returns false
if this list is empty, true
otherwise.
*
* @return false
if this list is empty, true
otherwise.
*/
public final boolean isNotEmpty() {
return this instanceof Cons;
}
public final B uncons(final F2, B> cons, final B nil) {
return isEmpty() ? nil : cons.f(head(), tail());
}
/**
* Returns the head of this list if there is one or the given argument if this list is empty.
*
* @param a The argument to return if this list is empty.
* @return The head of this list if there is one or the given argument if this list is empty.
*/
public final A orHead(final F0 a) {
return isEmpty() ? a.f() : head();
}
/**
* Returns the tail of this list if there is one or the given argument if this list is empty.
*
* @param as The argument to return if this list is empty.
* @return The tail of this list if there is one or the given argument if this list is empty.
*/
public final List orTail(final F0> as) {
return isEmpty() ? as.f() : tail();
}
/**
* Returns the head of the list, if any.
*
* @return The optional head of the list.
*/
public final Option headOption() {
return isEmpty() ? Option.none() : some(head());
}
/**
* Returns an either projection of this list; the given argument in Left
if empty, or
* the first element in Right
.
*
* @param x The value to return in left if this list is empty.
* @return An either projection of this list.
*/
public final Either toEither(final F0 x) {
return isEmpty() ? Either.left(x.f()) : Either.right(head());
}
/**
* Returns a stream projection of this list.
*
* @return A stream projection of this list.
*/
public final Stream toStream() {
return isEmpty() ? Stream.nil() : Stream.cons(head(), () -> tail().toStream());
}
/**
* Returns a array projection of this list.
*
* @return A array projection of this list.
*/
@SuppressWarnings("unchecked")
public final Array toArray() {
return mkArray(toArrayObject());
}
public final Object[] toArrayObject() {
final int length = length();
final Object[] a = new Object[length];
List x = this;
for (int i = 0; i < length; i++) {
a[i] = x.head();
x = x.tail();
}
return a;
}
/**
* Returns a array projection of this list.
*
* @param c The class type of the array to return.
* @return A array projection of this list.
*/
@SuppressWarnings({"unchecked", "UnnecessaryFullyQualifiedName"})
public final Array toArray(final Class c) {
final A[] a = (A[]) java.lang.reflect.Array.newInstance(c.getComponentType(), length());
List x = this;
for (int i = 0; i < length(); i++) {
a[i] = x.head();
x = x.tail();
}
return Array.array(a);
}
/**
* Returns an array from this list.
*
* @param c The class type of the array to return.
* @return An array from this list.
*/
public final A[] array(final Class c) {
return toArray(c).array(c);
}
/**
* Prepends (cons) the given element to this list to product a new list.
*
* @param a The element to prepend.
* @return A new list with the given element at the head.
*/
public final List cons(final A a) {
return new Cons<>(a, this);
}
/**
* Prepends (cons) the given element to this list to product a new list. This method is added to prevent conflict with
* overloads.
*
* @param a The element to prepend.
* @return A new list with the given element at the head.
*/
public final List conss(final A a) {
return new Cons<>(a, this);
}
/**
* Maps the given function across this list.
*
* @param f The function to map across this list.
* @return A new list after the given function has been applied to each element.
*/
public final List map(final F f) {
final Buffer bs = empty();
for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) {
bs.snoc(f.f(xs.head()));
}
return bs.toList();
}
/**
* Performs a side-effect for each element of this list.
*
* @param f The side-effect to perform for the given element.
* @return The unit value.
*/
public final Unit foreach(final F f) {
for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) {
f.f(xs.head());
}
return unit();
}
/**
* Performs a side-effect for each element of this list.
*
* @param f The side-effect to perform for the given element.
*/
public final void foreachDoEffect(final Effect1 f) {
for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) {
f.f(xs.head());
}
}
/**
* Filters elements from this list by returning only elements which produce true
when
* the given function is applied to them.
*
* @param f The predicate function to filter on.
* @return A new list whose elements all match the given predicate.
*/
public final List filter(final F f) {
final Buffer b = empty();
for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) {
final A h = xs.head();
if (f.f(h)) {
b.snoc(h);
}
}
return b.toList();
}
/**
* Filters elements from this list by returning only elements which produce false
when
* the given function is applied to them.
*
* @param f The predicate function to filter on.
* @return A new list whose elements do not match the given predicate.
*/
public final List removeAll(final F f) {
return filter(compose(not, f));
}
/**
* Removes the first element that equals the given object.
* To remove all matches, use removeAll(e.eq(a))
*
* @param a The element to remove
* @param e An Equals
instance for the element's type.
* @return A new list whose elements do not match the given predicate.
*/
public final List delete(final A a, final Equal e) {
final P2, List> p = span(compose(not, e.eq(a)));
return p._2().isEmpty() ? p._1() : p._1().append(p._2().tail());
}
/**
* Returns the first elements of the head of this list that match the given predicate function.
*
* @param f The predicate function to apply on this list until it finds an element that does not
* hold, or the list is exhausted.
* @return The first elements of the head of this list that match the given predicate function.
*/
public final List takeWhile(final F f) {
final Buffer b = empty();
boolean taking = true;
for (List xs = this; xs.isNotEmpty() && taking; xs = xs.tail()) {
final A h = xs.head();
if (f.f(h)) {
b.snoc(h);
} else {
taking = false;
}
}
return b.toList();
}
/**
* Removes elements from the head of this list that do not match the given predicate function
* until an element is found that does match or the list is exhausted.
*
* @param f The predicate function to apply through this list.
* @return The list whose first element does not match the given predicate function.
*/
public final List dropWhile(final F f) {
List xs;
//noinspection StatementWithEmptyBody
for (xs = this; xs.isNotEmpty() && f.f(xs.head()); xs = xs.tail()) ;
return xs;
}
/**
* Returns a tuple where the first element is the longest prefix of this list that satisfies
* the given predicate and the second element is the remainder of the list.
*
* @param p A predicate to be satisfied by a prefix of this list.
* @return A tuple where the first element is the longest prefix of this list that satisfies
* the given predicate and the second element is the remainder of the list.
*/
public final P2, List> span(final F p) {
final Buffer b = empty();
for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) {
if (p.f(xs.head()))
b.snoc(xs.head());
else
return p(b.toList(), xs);
}
return p(b.toList(), List.nil());
}
/**
* Returns a tuple where the first element is the longest prefix of this list that does not satisfy
* the given predicate and the second element is the remainder of the list.
*
* @param p A predicate for an element to not satisfy by a prefix of this list.
* @return A tuple where the first element is the longest prefix of this list that does not satisfy
* the given predicate and the second element is the remainder of the list.
*/
public final P2, List> breakk(final F p) {
return span(a -> !p.f(a));
}
/**
* Groups elements according to the given equality implementation by longest
* sequence of equal elements.
*
* @param e The equality implementation for the elements.
* @return A list of grouped elements.
*/
public final List> group(final Equal e) {
if (isEmpty())
return nil();
else {
final P2, List> z = tail().span(e.eq(head()));
return cons(z._1().cons(head()), z._2().group(e));
}
}
/**
* Binds the given function across each element of this list with a final join.
*
* @param f The function to apply to each element of this list.
* @return A new list after performing the map, then final join.
*/
public final List bind(final F> f) {
final Buffer b = empty();
for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) {
b.append(f.f(xs.head()));
}
return b.toList();
}
/**
* Binds the given function across each element of this list and the given list with a final
* join.
*
* @param lb A given list to bind the given function with.
* @param f The function to apply to each element of this list and the given list.
* @return A new list after performing the map, then final join.
*/
public final List bind(final List lb, final F> f) {
return lb.apply(map(f));
}
/**
* Binds the given function across each element of this list and the given list with a final
* join.
*
* @param lb A given list to bind the given function with.
* @param f The function to apply to each element of this list and the given list.
* @return A new list after performing the map, then final join.
*/
public final List bind(final List lb, final F2 f) {
return bind(lb, curry(f));
}
/**
* Promotes the given function of arity-2 to a function on lists.
*
* @param f The function to promote to a function on lists.
* @return The given function, promoted to operate on lists.
*/
public static F, F, List>> liftM2(final F> f) {
return curry((as, bs) -> as.bind(bs, f));
}
/**
* Binds the given function across each element of this list and the given lists with a final
* join.
*
* @param lb A given list to bind the given function with.
* @param lc A given list to bind the given function with.
* @param f The function to apply to each element of this list and the given lists.
* @return A new list after performing the map, then final join.
*/
public final List bind(final List lb, final List lc, final F>> f) {
return lc.apply(bind(lb, f));
}
/**
* Binds the given function across each element of this list and the given lists with a final
* join.
*
* @param lb A given list to bind the given function with.
* @param lc A given list to bind the given function with.
* @param ld A given list to bind the given function with.
* @param f The function to apply to each element of this list and the given lists.
* @return A new list after performing the map, then final join.
*/
public final List bind(final List lb, final List lc, final List ld,
final F>>> f) {
return ld.apply(bind(lb, lc, f));
}
/**
* Binds the given function across each element of this list and the given lists with a final
* join.
*
* @param lb A given list to bind the given function with.
* @param lc A given list to bind the given function with.
* @param ld A given list to bind the given function with.
* @param le A given list to bind the given function with.
* @param f The function to apply to each element of this list and the given lists.
* @return A new list after performing the map, then final join.
*/
public final List bind(final List lb, final List lc, final List ld, final List le,
final F>>>> f) {
return le.apply(bind(lb, lc, ld, f));
}
/**
* Binds the given function across each element of this list and the given lists with a final
* join.
*
* @param lb A given list to bind the given function with.
* @param lc A given list to bind the given function with.
* @param ld A given list to bind the given function with.
* @param le A given list to bind the given function with.
* @param lf A given list to bind the given function with.
* @param f The function to apply to each element of this list and the given lists.
* @return A new list after performing the map, then final join.
*/
public final List bind(final List lb, final List lc, final List ld, final List le,
final List lf, final F>>>>> f) {
return lf.apply(bind(lb, lc, ld, le, f));
}
/**
* Binds the given function across each element of this list and the given lists with a final
* join.
*
* @param lb A given list to bind the given function with.
* @param lc A given list to bind the given function with.
* @param ld A given list to bind the given function with.
* @param le A given list to bind the given function with.
* @param lf A given list to bind the given function with.
* @param lg A given list to bind the given function with.
* @param f The function to apply to each element of this list and the given lists.
* @return A new list after performing the map, then final join.
*/
public final List bind(final List lb, final List lc, final List ld, final List le,
final List lf, final List lg,
final F>>>>>> f) {
return lg.apply(bind(lb, lc, ld, le, lf, f));
}
/**
* Binds the given function across each element of this list and the given lists with a final
* join.
*
* @param lb A given list to bind the given function with.
* @param lc A given list to bind the given function with.
* @param ld A given list to bind the given function with.
* @param le A given list to bind the given function with.
* @param lf A given list to bind the given function with.
* @param lg A given list to bind the given function with.
* @param lh A given list to bind the given function with.
* @param f The function to apply to each element of this list and the given lists.
* @return A new list after performing the map, then final join.
*/
public final List bind(final List lb, final List lc, final List ld, final List le,
final List lf, final List lg, final List lh,
final F>>>>>>> f) {
return lh.apply(bind(lb, lc, ld, le, lf, lg, f));
}
/**
* Performs a bind across each list element, but ignores the element value each time.
*
* @param bs The list to apply in the final join.
* @return A new list after the final join.
*/
public final List sequence(final List bs) {
final F> c = constant(bs);
return bind(c);
}
/**
* Sequence the given list and collect the output on the right side of an either.
*
* @param list the given list
* @param the type of the right value
* @param the type of the left value
* @return the either
*/
public static final Either> sequenceEither(final List> list) {
return list.traverseEither(identity());
}
/**
* Sequence the given list and collect the output on the left side of an either.
*
* @param list the given list
* @param the type of the right value
* @param the type of the left value
* @return the either
*/
public static final Either, R> sequenceEitherLeft(final List> list) {
return list.traverseEitherLeft(identity());
}
/**
* Sequence the given list and collect the output on the right side of an either.
*
* @param list the given list
* @param the type of the right value
* @param the type of the left value
* @return the either
*/
public static final Either> sequenceEitherRight(final List> list) {
return list.traverseEitherRight(identity());
}
/**
* Sequence the given list and collect the output as a function.
*
* @param list the given list
* @param the type of the input value
* @param the type of the output value
* @return the either
*/
public static final F> sequenceF(final List> list) {
return list.traverseF(identity());
}
/**
* Sequence the given list and collect the output as an IO.
*
* @param list the given list
* @param the type of the IO value
* @return the IO
*/
public static final IO> sequenceIO(final List> list) {
return list.traverseIO(identity());
}
/**
* Sequence the given list and collect the output as an list.
*
* @param list the given list
* @param the type of the list value
* @return the list
*/
public static final List> sequenceList(final List> list) {
return list.traverseList(identity());
}
/**
* Sequence the given list and collect the output as an list.
*
* @param list the given list
* @param the type of the list value
* @return the list
*/
public static final Option> sequenceOption(final List