fj.data.IterableW 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.data.Option.some;
import static fj.data.Stream.iterableStream;
import fj.Equal;
import fj.F;
import fj.F2;
import fj.P2;
import static fj.Function.curry;
import static fj.Function.identity;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
* A wrapper for Iterable that equips it with some useful functions.
*/
public final class IterableW implements Iterable {
private final Iterable i;
private IterableW(final Iterable i) {
this.i = i;
}
/**
* Wraps the given iterable.
*
* @param a The iterable to wrap.
* @return An iterable equipped with some useful functions.
*/
public static IterableW wrap(final Iterable a) {
return new IterableW<>(a);
}
/**
* Provides a function that wraps the given iterable.
*
* @return A function that returns the given iterable, wrapped.
*/
public static > F> wrap() {
return IterableW::wrap;
}
/**
* Returns an Iterable that completely preserves the argument. The unit function for Iterables.
*
* @param a A value to preserve in an Iterable.
* @return An Iterable that yields the argument when iterated over.
*/
public static IterableW iterable(final A a) {
return wrap(some(a));
}
/**
* Wraps a given function's return value in a Iterable.
* The Kleisli arrow for Iterables.
*
* @param f The function whose return value to wrap in a Iterable.
* @return The equivalent function whose return value is iterable.
*/
public static F> iterable(final F f) {
return a -> iterable(f.f(a));
}
/**
* Provides a transformation from a function to a Iterable-valued function that is equivalent to it.
* The first-class Kleisli arrow for Iterables.
*
* @return A transformation from a function to the equivalent Iterable-valued function.
*/
public static F, F>> arrow() {
return IterableW::iterable;
}
/**
* Binds the given function across the wrapped Iterable with a final join.
*
* @param f A function to bind across the Iterable.
* @return an iterable result of binding the given function over the wrapped Iterable.
*/
public > IterableW bind(final F f) {
return wrap(iterableStream(this).bind(a -> iterableStream(f.f(a))));
}
/**
* Performs function application within an iterable (applicative functor pattern).
*
* @param f The iterable function to apply.
* @return A new iterable after applying the given iterable function to the wrapped iterable.
*/
public IterableW apply(final Iterable> f) {
return wrap(f).bind(this::map);
}
/**
* Binds the given function to the values in the given iterables with a final join.
*
* @param a A given iterable to bind the given function with.
* @param b A given iterable to bind the given function with.
* @param f The function to apply to the values in the given iterables.
* @return A new iterable after performing the map, then final join.
*/
public static IterableW bind(final Iterable a, final Iterable b, final F> f) {
return wrap(b).apply(wrap(a).map(f));
}
/**
* Promotes a function of arity-2 to a function on iterables.
*
* @param f The function to promote.
* @return A function of arity-2 promoted to map over iterables.
*/
public static F, F, IterableW>> liftM2(final F> f) {
return curry((ca, cb) -> bind(ca, cb, f));
}
/**
* Performs a bind across each element of all iterables of an iterable, collecting the values in an iterable.
* This implementation is strict and requires O(n) stack space.
*
* @param as The iterable of iterables to transform.
* @return A iterable of iterables containing the results of the bind operations across all given iterables.
*/
public static > IterableW> sequence(final Iterable as) {
final Stream ts = iterableStream(as);
return ts.isEmpty() ?
iterable(wrap(Option.none())) :
wrap(ts.head()).bind(a ->
sequence(ts.tail().map(IterableW.wrap())._1()).bind(as2 ->
iterable(wrap(Stream.cons(a, () -> iterableStream(as2))))
)
);
}
/**
* The first-class bind function over Iterable.
* Returns a function that binds a given function across a given iterable.
*
* @return a function that binds a given function across a given iterable.
*/
public static > F, F, IterableW>> bind() {
return a -> a::bind;
}
/**
* Joins an Iterable of Iterables into a single Iterable.
*
* @param as An Iterable of Iterables to join.
* @return the joined Iterable.
*/
public static > IterableW join(final Iterable as) {
final F id = identity();
return wrap(as).bind(id);
}
/**
* Returns a function that joins an Iterable of Iterables into a single Iterable.
*
* @return a function that joins an Iterable of Iterables into a single Iterable.
*/
public static > F, IterableW> join() {
return IterableW::join;
}
/**
* Maps a given function across the wrapped Iterable.
*
* @param f A function to map across the wrapped Iterable.
* @return An Iterable of the results of mapping the given function across the wrapped Iterable.
*/
public IterableW map(final F f) {
return bind(iterable(f));
}
/**
* Returns a function that promotes any function so that it operates on Iterables.
*
* @return a function that promotes any function so that it operates on Iterables.
*/
public static F, F, IterableW>> map() {
return f -> a -> a.map(f);
}
/**
* The catamorphism for Iterables, implemented as a left fold.
*
* @param f The function with which to fold the wrapped iterable.
* @param z The base case value of the destination type, applied first (leftmost) to the fold.
* @return The result of the catamorphism.
*/
public B foldLeft(final F> f, final B z) {
B p = z;
for (final A x : this) {
p = f.f(p).f(x);
}
return p;
}
/**
* Takes the first 2 elements of the iterable 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 iterable.
* @return The final result after the left-fold reduction.
*/
public A foldLeft1(final F2 f) {
return foldLeft1(curry(f));
}
/**
* Takes the first 2 elements of the iterable 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 iterable.
* @return The final result after the left-fold reduction.
*/
public A foldLeft1(final F> f) {
return iterableStream(this).foldLeft1(f);
}
/**
* The catamorphism for Iterables, implemented as a right fold.
*
* @param f The function with which to fold the wrapped iterable.
* @param z The base case value of the destination type, applied last (rightmost) to the fold.
* @return The result of the catamorphism.
*/
public B foldRight(final F2 f, final B z) {
final F id = identity();
return foldLeft(curry((k, a, b) -> k.f(f.f(a, b))), id).f(z);
}
/**
* Returns an iterator for this iterable.
*
* @return an iterator for this iterable.
*/
public Iterator iterator() {
return i.iterator();
}
/**
* Zips this iterable with the given iterable of functions, applying each function in turn to the
* corresponding element in this iterable to produce a new iterable. The iteration is normalised
* so that it ends when one of the iterators is exhausted.
*
* @param fs The iterable of functions to apply to this iterable.
* @return A new iterable with the results of applying the functions to this iterable.
*/
public IterableW zapp(final Iterable> fs) {
return wrap(iterableStream(this).zapp(iterableStream(fs)));
}
/**
* Zips this iterable with the given iterable using the given function to produce a new iterable. If
* this iterable and the given iterable have different lengths, then the longer iterable is normalised
* so this function never fails.
*
* @param bs The iterable to zip this iterable with.
* @param f The function to zip this iterable and the given iterable with.
* @return A new iterable with a length the same as the shortest of this iterable and the given
* iterable.
*/
public Iterable zipWith(final Iterable bs, final F> f) {
return wrap(iterableStream(this).zipWith(iterableStream(bs), f));
}
/**
* Zips this iterable with the given iterable using the given function to produce a new iterable. If
* this iterable and the given iterable have different lengths, then the longer iterable is normalised
* so this function never fails.
*
* @param bs The iterable to zip this iterable with.
* @param f The function to zip this iterable and the given iterable with.
* @return A new iterable with a length the same as the shortest of this iterable and the given
* iterable.
*/
public Iterable zipWith(final Iterable bs, final F2 f) {
return zipWith(bs, curry(f));
}
/**
* Zips this iterable with the given iterable to produce a iterable of pairs. If this iterable and the
* given iterable have different lengths, then the longer iterable is normalised so this function
* never fails.
*
* @param bs The iterable to zip this iterable with.
* @return A new iterable with a length the same as the shortest of this iterable and the given
* iterable.
*/
public Iterable> zip(final Iterable bs) {
return wrap(iterableStream(this).zip(iterableStream(bs)));
}
/**
* Zips this iterable with the index of its element as a pair.
*
* @return A new iterable with the same length as this iterable.
*/
public Iterable> zipIndex() {
return wrap(iterableStream(this).zipIndex());
}
/**
* Returns a java.util.List implementation for this iterable.
* The returned list cannot be modified.
*
* @return An immutable implementation of java.util.List for this iterable.
*/
public List toStandardList() {
return new List() {
public int size() {
return iterableStream(IterableW.this).length();
}
public boolean isEmpty() {
return iterableStream(IterableW.this).isEmpty();
}
@SuppressWarnings("unchecked")
public boolean contains(final Object o) {
return iterableStream(IterableW.this).exists(Equal.anyEqual().eq((A) o));
}
public Iterator iterator() {
return IterableW.this.iterator();
}
public Object[] toArray() {
return Array.iterableArray(iterableStream(IterableW.this)).array();
}
@SuppressWarnings("SuspiciousToArrayCall")
public T[] toArray(final T[] a) {
return iterableStream(IterableW.this).toCollection().toArray(a);
}
public boolean add(final A a) {
return false;
}
public boolean remove(final Object o) {
return false;
}
public boolean containsAll(final Collection> c) {
return iterableStream(IterableW.this).toCollection().containsAll(c);
}
public boolean addAll(final Collection extends A> c) {
return false;
}
public boolean addAll(final int index, final Collection extends A> c) {
return false;
}
public boolean removeAll(final Collection> c) {
return false;
}
public boolean retainAll(final Collection> c) {
return false;
}
public void clear() {
throw new UnsupportedOperationException("Modifying an immutable List.");
}
public A get(final int index) {
return iterableStream(IterableW.this).index(index);
}
public A set(final int index, final A element) {
throw new UnsupportedOperationException("Modifying an immutable List.");
}
public void add(final int index, final A element) {
throw new UnsupportedOperationException("Modifying an immutable List.");
}
public A remove(final int index) {
throw new UnsupportedOperationException("Modifying an immutable List.");
}
public int indexOf(final Object o) {
int i = -1;
for (final A a : IterableW.this) {
i++;
if (a.equals(o))
return i;
}
return i;
}
public int lastIndexOf(final Object o) {
int i = -1;
int last = -1;
for (final A a : IterableW.this) {
i++;
if (a.equals(o))
last = i;
}
return last;
}
public ListIterator listIterator() {
return toListIterator(toZipper());
}
public ListIterator listIterator(final int index) {
return toListIterator(toZipper().bind(Zipper.move().f(index)));
}
public List subList(final int fromIndex, final int toIndex) {
return wrap(iterableStream(IterableW.this).drop(fromIndex).take(toIndex - fromIndex)).toStandardList();
}
private ListIterator toListIterator(final Option> z) {
return new ListIterator() {
private Option> pz = z;
public boolean hasNext() {
return pz.isSome() && !pz.some().atEnd();
}
public A next() {
if (pz.isSome())
pz = pz.some().next();
else throw new NoSuchElementException();
if (pz.isSome())
return pz.some().focus();
else throw new NoSuchElementException();
}
public boolean hasPrevious() {
return pz.isSome() && !pz.some().atStart();
}
public A previous() {
pz = pz.some().previous();
return pz.some().focus();
}
public int nextIndex() {
return pz.some().index() + (pz.some().atEnd() ? 0 : 1);
}
public int previousIndex() {
return pz.some().index() - (pz.some().atStart() ? 0 : 1);
}
public void remove() {
throw new UnsupportedOperationException("Remove on immutable ListIterator");
}
public void set(final A a) {
throw new UnsupportedOperationException("Set on immutable ListIterator");
}
public void add(final A a) {
throw new UnsupportedOperationException("Add on immutable ListIterator");
}
};
}
};
}
public Option> toZipper() {
return Zipper.fromStream(iterableStream(this));
}
}