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
package fj.data;
import static fj.Bottom.error;
import fj.Equal;
import fj.F2Functions;
import fj.Hash;
import fj.Monoid;
import fj.Ord;
import fj.Ordering;
import fj.P;
import fj.P1;
import fj.Show;
import fj.Unit;
import fj.P2;
import fj.F0;
import fj.F;
import fj.F2;
import fj.Function;
import static fj.Function.*;
import static fj.P.p;
import static fj.P.p2;
import static fj.Unit.unit;
import static fj.data.Array.mkArray;
import static fj.data.List.Buffer.*;
import static fj.data.Option.none;
import static fj.data.Option.some;
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;
import static fj.Ordering.GT;
import static fj.Ord.intOrd;
import fj.control.Trampoline;
import fj.control.parallel.Promise;
import fj.control.parallel.Strategy;
import fj.data.optic.Optional;
import fj.data.optic.PTraversal;
import fj.data.optic.Prism;
import fj.data.optic.Traversal;
import fj.data.vector.V2;
import fj.function.Effect1;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Provides an in-memory, immutable, singly linked list.
*
* @version %build.number%
*/
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;
}
/**
* Performs a reduction on this list using the given arguments.
* @deprecated As of release 4.5, use {@link #uncons}
*
* @param nil The value to return if this list is empty.
* @param cons The function to apply to the head and tail of this list if it is not empty.
* @return A reduction on this list.
*/
@Deprecated
public final B list(final B nil, final F, B>> cons) {
return uncons(uncurryF2(cons), nil);
}
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 an option projection of this list; None
if empty, or the first element in
* Some
. Equivalent to {@link #headOption()}.
* @deprecated As of release 4.5, use {@link #headOption()}
* @return An option projection of this list.
*/
@Deprecated
public final Option toOption() {
return headOption();
}
/**
* Returns the head of the list, if any. Equivalent to {@link #toOption()} .
*
* @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;
}
/**
* To be removed in future release:
* affectation of the result of this method to a non generic array
* will result in runtime error (ClassCastException).
*
* @deprecated As of release 4.6, use {@link #array(Class)}.
*/
@SuppressWarnings("unchecked")
@Deprecated
public final A[] toJavaArray() {
return (A[]) toArrayObject();
}
/**
* 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);
}
/**
* Traverses through the List with the given function
*
* @param f The function that produces Option value
* @return none if applying f returns none to any element of the list or f mapped list in some .
*/
public final Option> traverseOption(final F> f) {
return foldRight(
(a, obs) -> f.f(a).bind(o -> obs.map(os -> os.cons(o))),
some(List.nil())
);
}
/**
* Traverse through the List with given function.
*
* @param f The function that produces Either value.
* @return error in left or f mapped list in right.
*/
public final Either> traverseEither(final F> f) {
return foldRight(
(a, acc) -> f.f(a).right().bind(e -> acc.right().map(es -> es.cons(e))),
Either.right(List.nil())
);
}
public final Stream> traverseStream(final F> f) {
return foldRight(
(a, acc) -> f.f(a).bind(s -> acc.map(ss -> ss.cons(s))),
Stream.nil()
);
}
public final P1> traverseP1(final F> f){
return foldRight(
(a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))),
p(List.nil())
);
}
public final IO> traverseIO(F> f) {
return this.foldRight(
(a, acc) -> IOFunctions.bind(f.f(a), b -> IOFunctions.map(acc, bs -> bs.cons(b))),
IOFunctions.unit(List.nil())
);
}
public final F> traverseF(F> f) {
return this.foldRight(
(a, acc) -> Function.bind(acc,
(bs) -> Function.compose(bs::cons, f.f(a))),
constant(List.nil())
);
}
public final Trampoline> traverseTrampoline(final F> f) {
return foldRight(
(a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))),
Trampoline.pure(List.nil()));
}
public final Promise> traversePromise(final F> f) {
return foldRight(
(a, acc) -> f.f(a).bind(b -> acc.fmap(bs -> bs.cons(b))),
Promise.promise(Strategy.idStrategy(), p(List.nil())));
}
public final List> traverseList(final F> f) {
return foldRight(
(a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))),
single(List.nil()));
}
public final Validation> traverseValidation(final F> f) {
return foldRight(
(a, acc) -> f.f(a).bind(b -> acc.map(bs -> bs.cons(b))),
Validation.success(List.nil()));
}
public final V2> traverseV2(final F> f) {
return foldRight(
(a, acc) -> acc.apply(f.f(a)., List>> map(e -> es -> es.cons(e))),
v(List.nil(), List.nil()));
}
/**
* Performs function application within a list (applicative functor pattern).
*
* @param lf The list of functions to apply.
* @return A new list after applying the given list of functions through this list.
*/
public final List apply(final List> lf) {
return lf.bind(this::map);
}
/**
* Appends the given list to this list.
*
* @param as The list to append to this one.
* @return A new list that has appended the given list.
*/
public final List append(final List as) {
return fromList(this).prependToList(as);
}
/**
* Performs a right-fold reduction across this list.
*
* @param f The function to apply on each element of the list.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public final B foldRight(final F> f, final B b) {
return reverse().foldLeft(flip(f), b);
}
/**
* Performs a right-fold reduction across this list. This function uses O(length) stack space.
*
* @param f The function to apply on each element of the list.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public final B foldRight(final F2 f, final B b) {
return foldRight(curry(f), b);
}
/**
* Performs a right-fold reduction across this list in O(1) stack space.
* @param f The function to apply on each element of the list.
* @param b The beginning value to start the application from.
* @return A Trampoline containing the final result after the right-fold reduction.
*/
public final Trampoline foldRightC(final F2 f, final B b) {
return Trampoline.suspend(P.lazy(() -> isEmpty() ? Trampoline.pure(b) : tail().foldRightC(f, b).map(F2Functions.f(f, head()))));
}
/**
* Performs a left-fold reduction across this list. This function runs in constant space.
*
* @param f The function to apply on each element of the list.
* @param b The beginning value to start the application from.
* @return The final result after the left-fold reduction.
*/
public final B foldLeft(final F> f, final B b) {
return foldLeft(uncurryF2(f), b);
}
/**
* Performs a left-fold reduction across this list. This function runs in constant space.
*
* @param f The function to apply on each element of the list.
* @param b The beginning value to start the application from.
* @return The final result after the left-fold reduction.
*/
public final B foldLeft(final F2 f, final B b) {
B x = b;
for (List xs = this; !xs.isEmpty(); xs = xs.tail()) {
x = f.f(x, xs.head());
}
return x;
}
/**
* Takes the first 2 elements of the list and applies the function to them,
* then applies the function to the result and the third element and so on.
*
* @param f The function to apply on each element of the list.
* @return The final result after the left-fold reduction.
*/
public final A foldLeft1(final F2 f) {
if (isEmpty())
throw error("Undefined: foldLeft1 on empty list");
return tail().foldLeft(f, head());
}
/**
* Takes the first 2 elements of the list and applies the function to them,
* then applies the function to the result and the third element and so on.
*
* @param f The function to apply on each element of the list.
* @return The final result after the left-fold reduction.
*/
public final A foldLeft1(final F> f) {
return foldLeft1(uncurryF2(f));
}
/**
* Reverse this list in constant stack space.
*
* @return A new list that is the reverse of this one.
*/
public final List reverse() {
return foldLeft((as, a) -> cons(a, as), nil());
}
/**
* Returns the element at the given index if it exists, fails otherwise.
*
* @param i The index at which to get the element to return.
* @return The element at the given index if it exists, fails otherwise.
*/
public final A index(final int i) {
if (i < 0 || i > length() - 1)
throw error("index " + i + " out of range on list with length " + length());
else {
List xs = this;
for (int c = 0; c < i; c++) {
xs = xs.tail();
}
return xs.head();
}
}
/**
* Takes the given number of elements from the head of this list if they are available.
*
* @param i The maximum number of elements to take from this list.
* @return A new list with a length the same, or less than, this list.
*/
public final List take(final int i) {
Buffer result = empty();
List list = this;
int index = i;
while (index > 0 && list.isNotEmpty()) {
result.snoc(list.head());
list = list.tail();
index--;
}
return result.toList();
}
/**
* Drops the given number of elements from the head of this list if they are available.
*
* @param i The number of elements to drop from the head of this list.
* @return A list with a length the same, or less than, this list.
*/
public final List drop(final int i) {
List xs = this;
for (int c = 0; xs.isNotEmpty() && c < i; xs = xs.tail())
c++;
return xs;
}
/**
* Splits this list into two lists at the given index. If the index goes out of bounds, then it is
* normalised so that this function never fails.
*
* @param i The index at which to split this list in two parts.
* @return A pair of lists split at the given index of this list.
*/
public final P2, List> splitAt(final int i) {
int c = 0;
List first = List.nil();
List second = nil();
for (List xs = this; xs.isNotEmpty(); xs = xs.tail()) {
final A h = xs.head();
if (c < i) {
first = first.cons(h);
} else {
second = second.cons(h);
}
c++;
}
return p(first.reverse(), second.reverse());
}
/**
* Splits this list into lists of the given size. If the size of this list is not evenly divisible by
* the given number, the last partition will contain the remainder.
*
* @param n The size of the partitions into which to split this list.
* @return A list of sublists of this list, of at most the given size.
*/
public final List> partition(final int n) {
if (n < 1)
throw error("Can't create list partitions shorter than 1 element long.");
if (isEmpty())
throw error("Partition on empty list.");
return unfold(as -> as.isEmpty() ? Option.none() : some(as.splitAt(n)), this);
}
/**
* Partitions the list into a tuple where the first element contains the
* items that satisfy the the predicate f and the second element contains the
* items that does not. The relative order of the elements in the returned tuple
* is the same as the original list.
*
* @param f Predicate function.
*/
public final P2, List> partition(F f) {
P2, List> p2 = foldLeft((acc, a) ->
f.f(a) ? p(acc._1().cons(a), acc._2()) : p(acc._1(), acc._2().cons(a)),
p(nil(), nil())
);
return p(p2._1().reverse(), p2._2().reverse());
}
/**
* Returns the list of initial segments of this list, shortest first.
*
* @return The list of initial segments of this list, shortest first.
*/
public final List> inits() {
List> s = single(List.nil());
if (isNotEmpty())
s = s.append(tail().inits().map(List.cons().f(head())));
return s;
}
/**
* Returns the list of final segments of this list, longest first.
*
* @return The list of final segments of this list, longest first.
*/
public final List> tails() {
return isEmpty() ? single(List.nil()) : cons(this, tail().tails());
}
/**
* Sorts this list using the given order over elements using a merge sort algorithm.
*
* @param o The order over the elements of this list.
* @return A sorted list according to the given order.
*/
public final List sort(final Ord o) {
if (isEmpty())
return nil();
else if (tail().isEmpty())
return this;
else {
final class Merge {
List merge(List xs, List ys, final Ord o) {
final Buffer buf = empty();
while (true) {
if (xs.isEmpty()) {
buf.append(ys);
break;
}
if (ys.isEmpty()) {
buf.append(xs);
break;
}
final A x = xs.head();
final A y = ys.head();
if (o.isLessThanOrEqualTo(x, y)) {
buf.snoc(x);
xs = xs.tail();
} else {
buf.snoc(y);
ys = ys.tail();
}
}
return buf.toList();
}
}
final P2, List> s = splitAt(length() / 2);
return new Merge().merge(s._1().sort(o), s._2().sort(o), o);
}
}
/**
* Zips this list with the given list using the given function to produce a new list. If this list
* and the given list have different lengths, then the longer list is normalised so this function
* never fails.
*
* @param bs The list to zip this list with.
* @param f The function to zip this list and the given list with.
* @return A new list with a length the same as the shortest of this list and the given list.
*/
public final List zipWith(List bs, final F> f) {
final Buffer buf = empty();
List as = this;
while (as.isNotEmpty() && bs.isNotEmpty()) {
buf.snoc(f.f(as.head()).f(bs.head()));
as = as.tail();
bs = bs.tail();
}
return buf.toList();
}
/**
* Zips this list with the given list using the given function to produce a new list. If this list
* and the given list have different lengths, then the longer list is normalised so this function
* never fails.
*
* @param bs The list to zip this list with.
* @param f The function to zip this list and the given list with.
* @return A new list with a length the same as the shortest of this list and the given list.
*/
public final List zipWith(final List bs, final F2 f) {
return zipWith(bs, curry(f));
}
/**
* Provides a first-class version of zipWith
*
* @return The first-class version of zipWith
*/
public static F, F, F>, List>>> zipWith() {
return curry((as, bs, f) -> as.zipWith(bs, f));
}
/**
* Zips this list with the given list to produce a list of pairs. If this list and the given list
* have different lengths, then the longer list is normalised so this function never fails.
*
* @param bs The list to zip this list with.
* @return A new list with a length the same as the shortest of this list and the given list.
*/
public final List> zip(final List bs) {
final F>> __2 = p2();
return zipWith(bs, __2);
}
/**
* The first-class version of the zip function.
*
* @return A function that zips the given lists to produce a list of pairs.
*/
public static F, F, List>>> zip() {
return curry(List::zip);
}
/**
* Zips this list with the index of its element as a pair.
*
* @return A new list with the same length as this list.
*/
public final List> zipIndex() {
return zipWith(range(0, length()), a -> i -> p(a, i));
}
/**
* Appends (snoc) the given element to this list to produce a new list.
*
* @param a The element to append to this list.
* @return A new list with the given element appended.
*/
public final List snoc(final A a) {
return fromList(this).snoc(a).toList();
}
/**
* Returns true
if the predicate holds for all of the elements of this list,
* false
otherwise (true
for the empty list).
*
* @param f The predicate function to test on each element of this list.
* @return true
if the predicate holds for all of the elements of this list,
* false
otherwise.
*/
public final boolean forall(final F f) {
return isEmpty() || f.f(head()) && tail().forall(f);
}
/**
* Returns true
if the predicate holds for at least one of the elements of this list,
* false
otherwise (false
for the empty list).
*
* @param f The predicate function to test on the elements of this list.
* @return true
if the predicate holds for at least one of the elements of this
* list.
*/
public final boolean exists(final F f) {
return find(f).isSome();
}
/**
* Finds the first occurrence of an element that matches the given predicate or no value if no
* elements match.
*
* @param f The predicate function to test on elements of this list.
* @return The first occurrence of an element that matches the given predicate or no value if no
* elements match.
*/
public final Option find(final F f) {
for (List as = this; as.isNotEmpty(); as = as.tail()) {
if (f.f(as.head()))
return some(as.head());
}
return none();
}
/**
* Intersperses the given argument between each element of this list.
*
* @param a The separator to intersperse in this list.
* @return A list with the given separator interspersed.
*/
public final List intersperse(final A a) {
return isEmpty() || tail().isEmpty() ?
this :
cons(head(), tail().bind(a2 -> list(a, a2)));
}
/**
* Intersperses this list through the given list then joins the results.
*
* @param as The list to intersperse through.
* @return This list through the given list then joins the results.
*/
@SuppressWarnings("unchecked")
public final List intercalate(final List> as) {
return join(as.intersperse(this));
}
/**
* Removes duplicates according to object equality.
*
* @return A list without duplicates according to object equality.
*/
public final List nub() {
return nub(Equal.anyEqual());
}
/**
* Removes duplicates according to the given equality. Warning: O(n^2).
*
* @param eq Equality over the elements.
* @return A list without duplicates.
*/
public final List nub(final Equal eq) {
return isEmpty() ? this : cons(head(), tail().filter(a -> !eq.eq(a, head())).nub(eq));
}
/**
* Removes duplicates according to the given ordering. This function is O(n).
*
* @param o An ordering for the elements.
* @return A list without duplicates.
*/
@SuppressWarnings("unchecked")
public final List nub(final Ord o) {
return sort(o).group(o.equal()).map(List.head_());
}
/**
* First-class head function.
*
* @return A function that gets the head of a given list.
*/
public static F, A> head_() {
return List::head;
}
/**
* Reutrns the tail of the list, if any.
* @return The optional tail of the list.
*/
public final Option> tailOption() {
return isEmpty() ? none() : some(tail());
}
/**
* First-class tail function.
*
* @return A function that gets the tail of a given list.
*/
public static F, List> tail_() {
return List::tail;
}
/**
* Returns a new list of all the items in this list that do not appear in the given list.
*
* @param eq an equality for the items of the lists.
* @param xs a list to subtract from this list.
* @return a list of all the items in this list that do not appear in the given list.
*/
public final List minus(final Equal eq, final List xs) {
return removeAll(compose(Monoid.disjunctionMonoid.sumLeft(), xs.mapM(curry(eq.eq()))));
}
/**
* Maps the given function of arity-2 across this list and returns a function that applies all the resulting
* functions to a given argument.
*
* @param f A function of arity-2
* @return A function that, when given an argument, applies the given function to that argument and every element
* in this list.
*/
public final F> mapM(final F> f) {
return sequence_(map(f));
}
/**
* Maps the given function across this list by binding through the Option monad.
*
* @param f The function to apply through the this list.
* @return A possible list of values after binding through the Option monad.
*/
public final Option> mapMOption(final F> f) {
return traverseOption(f);
}
/**
* Maps the given function across this list by binding through the Trampoline monad.
*
* @param f The function to apply through the this list.
* @return A list of values in the Trampoline monad.
*/
public final Trampoline> mapMTrampoline(final F> f) {
return foldRight((a, bs) -> f.f(a).bind(b -> bs.map(bbs -> bbs.cons(b))), Trampoline.pure(List.nil()));
}
/**
* Returns the index of the first element in this list which is equal (by the given equality) to the
* query element, or None if there is no such element.
*
* @param e An equality for this list's elements.
* @param a A query element.
* @return The index of the first element in this list which is equal (by the given equality) to the
* query element, or None if there is no such element.
*/
public final Option elementIndex(final Equal e, final A a) {
return lookup(e, zipIndex(), a);
}
/**
* Returns the last element of this list. Undefined for the empty list.
*
* @return The last element of this list or throws an error if this list is empty.
*/
public final A last() {
A a = head();
for (List xs = tail(); xs.isNotEmpty(); xs = xs.tail())
a = xs.head();
return a;
}
/**
* Returns all but the last element of this list. Undefiend for the empty list.
*
* @return All but the last element of this list. Undefiend for the empty list.
*/
public final List init() {
List ys = this;
final Buffer a = empty();
while(ys.isNotEmpty() && ys.tail().isNotEmpty()) {
a.snoc(ys.head());
ys = ys.tail();
}
return a.toList();
}
/**
* Inserts the given element before the first element that is greater than or equal to it according
* to the given ordering.
*
* @param f An ordering function to compare elements.
* @param x The element to insert.
* @return A new list with the given element inserted before the first element that is greater than or equal to
* it according to the given ordering.
*/
public final List insertBy(final F> f, final A x) {
List ys = this;
Buffer xs = empty();
while (ys.isNotEmpty() && f.f(x).f(ys.head()) == GT) {
xs = xs.snoc(ys.head());
ys = ys.tail();
}
return xs.append(ys.cons(x)).toList();
}
/**
* Returns the most common element in this list.
*
* @param o An ordering for the elements of the list.
* @return The most common element in this list.
*/
public final A mode(final Ord o) {
return sort(o).group(o.equal()).maximum(intOrd.contramap(List.length_())).head();
}
/**
* Groups the elements of this list by a given keyFunction into a {@link TreeMap}.
* The ordering of the keys is determined by {@link fj.Ord#hashOrd()}.
*
* @param keyFunction The function to select the keys for the map.
* @return A TreeMap containing the keys with the accumulated list of matched elements.
*/
public final TreeMap> groupBy(final F keyFunction) {
return groupBy(keyFunction, Ord.hashOrd());
}
/**
* Groups the elements of this list by a given keyFunction into a {@link TreeMap}.
*
* @param keyFunction The function to select the keys for the map.
* @param keyOrd An order for the keys of the tree map.
* @return A TreeMap containing the keys with the accumulated list of matched elements.
*/
public final TreeMap> groupBy(final F keyFunction, final Ord keyOrd) {
return groupBy(keyFunction, identity(), keyOrd);
}
/**
* Groups the elements of this list by a given keyFunction into a {@link TreeMap} and transforms
* the matching elements with the given valueFunction. The ordering of the keys is determined by
* {@link fj.Ord#hashOrd()}.
*
* @param keyFunction The function to select the keys for the map.
* @param valueFunction The function to apply on each matching value.
* @return A TreeMap containing the keys with the accumulated list of matched and mapped elements.
*/
public final TreeMap> groupBy(
final F keyFunction,
final F valueFunction) {
return this.groupBy(keyFunction, valueFunction, Ord.hashOrd());
}
/**
* Groups the elements of this list by a given keyFunction into a {@link TreeMap} and transforms
* the matching elements with the given valueFunction. The ordering of the keys is determined by
* the keyOrd parameter.
*
* @param keyFunction The function to select the keys for the map.
* @param valueFunction The function to apply on each matching value.
* @param keyOrd An order for the keys of the tree map.
* @return A TreeMap containing the keys with the accumulated list of matched and mapped elements.
*/
public final TreeMap> groupBy(
final F keyFunction,
final F valueFunction,
final Ord keyOrd) {
return this.groupBy(keyFunction, valueFunction, List.nil(), List::cons, keyOrd);
}
/**
* Groups the elements of this list by a given keyFunction into a {@link TreeMap} and transforms
* the matching elements with the given valueFunction. The ordering of the keys is determined by
* the keyOrd parameter.
*
* @param keyFunction The function to select the keys for the map.
* @param valueFunction The function to apply on each matching value.
* @param monoid A monoid, which defines the accumulator for the values and the zero value.
* @param keyOrd An order for the keys of the tree map.
* @return A TreeMap containing the keys with the accumulated list of matched and mapped elements.
*/
public final TreeMap groupBy(
final F keyFunction,
final F valueFunction,
final Monoid monoid,
final Ord keyOrd) {
return groupBy(keyFunction, valueFunction, monoid.zero(),
uncurryF2(monoid.sum()), keyOrd);
}
/**
* Groups the elements of this list by a given keyFunction, applies the valueFunction and
* accumulates the mapped values with the given grouping accumulator function on the grouping
* identity.
*
* @param keyFunction The function to select the keys.
* @param valueFunction The function to apply on each element.
* @param groupingIdentity The identity, or start value, for the grouping.
* @param groupingAcc The accumulator to apply on each matching value.
* @param keyOrd An order for the keys of the tree map.
* @return A TreeMap containing the keys with the accumulated result of matched and mapped
* elements.
*/
public final TreeMap groupBy(
final F keyFunction,
final F valueFunction,
final D groupingIdentity,
final F2 groupingAcc,
final Ord keyOrd) {
return this.foldLeft((map, element) -> {
final B key = keyFunction.f(element);
final C value = valueFunction.f(element);
return map.set(key, map.get(key)
.map(existing -> groupingAcc.f(value, existing))
.orSome(groupingAcc.f(value, groupingIdentity)));
}, TreeMap.empty(keyOrd)
);
}
/**
* Returns whether or not all elements in the list are equal according to the given equality test.
*
* @param eq The equality test.
* @return Whether or not all elements in the list are equal according to the given equality test.
*/
public final boolean allEqual(final Equal eq) {
return isEmpty() || tail().isEmpty() || eq.eq(head(), tail().head()) && tail().allEqual(eq);
}
public final boolean isPrefixOf(final Equal eq, final List xs) {
final Iterator i = iterator();
final Iterator j = xs.iterator();
while (i.hasNext() && j.hasNext()) {
if (!eq.eq(i.next(), j.next())) {
return false;
}
}
return !i.hasNext();
}
public final boolean isSuffixOf(final Equal eq, final List xs) {
final Iterator i = iterator();
final Iterator j = xs.drop(xs.length() - length()).iterator();
while (i.hasNext() && j.hasNext()) {
if (!eq.eq(i.next(), j.next())) {
return false;
}
}
return !i.hasNext();
}
/**
* First-class length.
*
* @return A function that gets the length of a given list.
*/
public static F, Integer> length_() {
return List::length;
}
/**
* Returns the maximum element in this list according to the given ordering.
*
* @param o An ordering for the elements of the list.
* @return The maximum element in this list according to the given ordering.
*/
public final A maximum(final Ord o) {
return foldLeft1(o.max);
}
/**
* Returns the minimum element in this list according to the given ordering.
*
* @param o An ordering for the elements of the list.
* @return The minimum element in this list according to the given ordering.
*/
public final A minimum(final Ord o) {
return foldLeft1(o.min);
}
public final java.util.List toJavaList() {
return new java.util.LinkedList<>(toCollection());
}
/**
* Projects an immutable collection of this list.
*
* @return An immutable collection of this list.
*/
public final Collection toCollection() {
return new AbstractCollection() {
public Iterator iterator() {
return new Iterator() {
private List xs = List.this;
public boolean hasNext() {
return xs.isNotEmpty();
}
public A next() {
if (xs.isEmpty())
throw new NoSuchElementException();
else {
final A a = xs.head();
xs = xs.tail();
return a;
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public int size() {
return length();
}
};
}
private static final class Nil extends List {
public static final Nil