
fj.data.IterableW Maven / Gradle / Ivy
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));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy