fj.data.Stream 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 fj.Equal;
import fj.F0;
import fj.Hash;
import fj.Show;
import fj.F;
import fj.F2;
import fj.Function;
import fj.Monoid;
import fj.Ord;
import fj.P;
import fj.P1;
import fj.P2;
import fj.Unit;
import fj.control.parallel.Promise;
import fj.control.parallel.Strategy;
import fj.Ordering;
import fj.function.Effect1;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import static fj.Bottom.error;
import static fj.Function.compose;
import static fj.Function.constant;
import static fj.Function.curry;
import static fj.Function.flip;
import static fj.Function.identity;
import static fj.P.p;
import static fj.P.p2;
import static fj.Unit.unit;
import static fj.control.parallel.Promise.promise;
import static fj.data.Array.mkArray;
import static fj.data.Option.none;
import static fj.data.Option.some;
import static fj.function.Booleans.not;
import static fj.Ordering.EQ;
import static fj.Ordering.GT;
import static fj.Ordering.LT;
/**
* A lazy (not yet evaluated), immutable, singly linked list.
*
* @version %build.number%
*/
public abstract class Stream implements Iterable {
private Stream() {
}
/**
* Returns an iterator for this stream. This method exists to permit the use in a for
-each loop.
*
* @return A iterator for this stream.
*/
public final Iterator iterator() {
return toCollection().iterator();
}
/**
* The first element of the stream or fails for the empty stream.
*
* @return The first element of the stream or fails for the empty stream.
*/
public abstract A head();
/**
* The stream without the first element or fails for the empty stream.
*
* @return The stream without the first element or fails for the empty stream.
*/
public abstract P1> tail();
/**
* Returns true
if this stream is empty, false
otherwise.
*
* @return true
if this stream is empty, false
otherwise.
*/
public final boolean isEmpty() {
return this instanceof Nil;
}
/**
* Returns false
if this stream is empty, true
otherwise.
*
* @return false
if this stream is empty, true
otherwise.
*/
public final boolean isNotEmpty() {
return this instanceof Cons;
}
/**
* Performs a reduction on this stream using the given arguments.
*
* @param nil The value to return if this stream is empty.
* @param cons The function to apply to the head and tail of this stream if it is not empty.
* @return A reduction on this stream.
*/
public final B stream(final B nil, final F>, B>> cons) {
return isEmpty() ? nil : cons.f(head()).f(tail());
}
/**
* Performs a right-fold reduction across this stream. This function uses O(length) stack space.
*
* @param f The function to apply on each element of the stream.
* @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, B>> f, final B b) {
return isEmpty() ? b : f.f(head()).f(new P1() {
public B _1() {
return tail()._1().foldRight(f, b);
}
});
}
/**
* Performs a right-fold reduction across this stream. This function uses O(length) stack space.
*
* @param f The function to apply on each element of the stream.
* @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, B> f, final B b) {
return foldRight(curry(f), b);
}
/**
* Performs a right-fold reduction across this stream. This function uses O(length) stack space.
*
* @param f The function to apply on each element of the stream.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public final B foldRight1(final F> f, final B b) {
return foldRight(compose(Function., B, B>andThen().f(P1.__1()), f), b);
}
/**
* Performs a right-fold reduction across this stream. This function uses O(length) stack space.
*
* @param f The function to apply on each element of the stream.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public final B foldRight1(final F2 f, final B b) {
return foldRight1(curry(f), b);
}
/**
* Performs a left-fold reduction across this stream. This function runs in constant space.
*
* @param f The function to apply on each element of the stream.
* @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) {
B x = b;
for (Stream xs = this; !xs.isEmpty(); xs = xs.tail()._1())
x = f.f(x).f(xs.head());
return x;
}
/**
* Performs a left-fold reduction across this stream. This function runs in constant space.
*
* @param f The function to apply on each element of the stream.
* @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) {
return foldLeft(curry(f), b);
}
/**
* Takes the first 2 elements of the stream 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 stream.
* @return The final result after the left-fold reduction.
*/
public final A foldLeft1(final F2 f) {
return foldLeft1(curry(f));
}
/**
* Takes the first 2 elements of the stream 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 stream.
* @return The final result after the left-fold reduction.
*/
public final A foldLeft1(final F> f) {
if (isEmpty())
throw error("Undefined: foldLeft1 on empty list");
return tail()._1().foldLeft(f, head());
}
/**
* Returns the head of this stream if there is one or the given argument if this stream is empty.
*
* @param a The argument to return if this stream is empty.
* @return The head of this stream if there is one or the given argument if this stream is empty.
*/
public final A orHead(final F0 a) {
return isEmpty() ? a.f() : head();
}
/**
* Returns the tail of this stream if there is one or the given argument if this stream is empty.
*
* @param as The argument to return if this stream is empty.
* @return The tail of this stream if there is one or the given argument if this stream is empty.
*/
public final P1> orTail(final P1> as) {
return isEmpty() ? as : tail();
}
/**
* Intersperses the given value between each two elements of the stream.
*
* @param a The value to intersperse between values of the stream.
* @return A new stream with the given value between each two elements of the stream.
*/
public final Stream intersperse(final A a) {
return isEmpty() ? this : cons(head(), new P1>() {
public Stream _1() {
return prefix(a, tail()._1());
}
public Stream prefix(final A x, final Stream xs) {
return xs.isEmpty() ? xs : cons(x, p(cons(xs.head(), new P1>() {
public Stream _1() {
return prefix(a, xs.tail()._1());
}
})));
}
});
}
/**
* Maps the given function across this stream.
*
* @param f The function to map across this stream.
* @return A new stream after the given function has been applied to each element.
*/
public final Stream map(final F f) {
return isEmpty() ? Stream.nil() : cons(f.f(head()), new P1>() {
public Stream _1() {
return tail()._1().map(f);
}
});
}
/**
* Provides a first-class version of the map function.
*
* @return A function that maps a given function across a given stream.
*/
public static F, F, Stream>> map_() {
return f -> as -> as.map(f);
}
/**
* Performs a side-effect for each element of this stream.
*
* @param f The side-effect to perform for the given element.
* @return The unit value.
*/
public final Unit foreach(final F f) {
for (Stream xs = this; xs.isNotEmpty(); xs = xs.tail()._1())
f.f(xs.head());
return unit();
}
/**
* Performs a side-effect for each element of this stream.
*
* @param f The side-effect to perform for the given element.
*/
public final void foreachDoEffect(final Effect1 f) {
for (Stream xs = this; xs.isNotEmpty(); xs = xs.tail()._1())
f.f(xs.head());
}
/**
* Filters elements from this stream 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 stream whose elements all match the given predicate.
*/
public final Stream filter(final F f) {
final Stream as = dropWhile(not(f));
return as.isNotEmpty() ? cons(as.head(), new P1>() {
public Stream _1() {
return as.tail()._1().filter(f);
}
}) : as;
}
/**
* Appends the given stream to this stream.
*
* @param as The stream to append to this one.
* @return A new stream that has appended the given stream.
*/
public final Stream append(final Stream as) {
return isEmpty() ? as : cons(head(), new P1>() {
public Stream _1() {
return tail()._1().append(as);
}
});
}
/**
* Appends the given stream to this stream.
*
* @param as The stream to append to this one.
* @return A new stream that has appended the given stream.
*/
public final Stream append(final F0> as) {
return isEmpty() ? as.f() : cons(head(), new P1>() {
public Stream _1() {
return tail()._1().append(as);
}
});
}
/**
* Returns a new stream of all the items in this stream that do not appear in the given stream.
*
* @param eq an equality for the items of the streams.
* @param xs a list to subtract from this stream.
* @return a stream of all the items in this stream that do not appear in the given stream.
*/
public final Stream minus(final Equal eq, final Stream xs) {
return removeAll(compose(Monoid.disjunctionMonoid.sumLeftS(), xs.mapM(curry(eq.eq()))));
}
/**
* Filters elements from this stream 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 stream whose elements do not match the given predicate.
*/
public final Stream removeAll(final F f) {
return filter(compose(not, f));
}
/**
* Turn a stream of functions into a function returning a stream.
*
* @param fs The stream of functions to sequence into a single function that returns a stream.
* @return A function that, when given an argument, applies all the functions in the given stream to it
* and returns a stream of the results.
*/
public static F> sequence_(final Stream> fs) {
return fs.foldRight((baf, p1) -> Function.bind(baf, p1._1(), Function.curry((a, stream) -> cons(a, p(stream)))), Function
.>constant(Stream.nil()));
}
/**
* Maps the given function of arity-2 across this stream 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));
}
/**
* Binds the given function across each element of this stream with a final join.
*
* @param f The function to apply to each element of this stream.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final F> f) {
return foldRight(h -> (t -> f.f(h).append(t)), nil());
}
/**
* Binds the given function across each element of this stream and the given stream with a final
* join.
*
* @param sb A given stream to bind the given function with.
* @param f The function to apply to each element of this stream and the given stream.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final Stream sb, final F> f) {
return sb.apply(map(f));
}
/**
* Binds the given function across each element of this stream and the given stream with a final
* join.
*
* @param sb A given stream to bind the given function with.
* @param f The function to apply to each element of this stream and the given stream.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final Stream sb, final F2 f) {
return bind(sb, curry(f));
}
/**
* Binds the given function across each element of this stream and the given streams with a final
* join.
*
* @param sb A given stream to bind the given function with.
* @param sc A given stream to bind the given function with.
* @param f The function to apply to each element of this stream and the given streams.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final Stream sb, final Stream sc, final F>> f) {
return sc.apply(bind(sb, f));
}
/**
* Binds the given function across each element of this stream and the given streams with a final
* join.
*
* @param sb A given stream to bind the given function with.
* @param sc A given stream to bind the given function with.
* @param sd A given stream to bind the given function with.
* @param f The function to apply to each element of this stream and the given streams.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final Stream sb, final Stream sc, final Stream sd,
final F>>> f) {
return sd.apply(bind(sb, sc, f));
}
/**
* Binds the given function across each element of this stream and the given streams with a final
* join.
*
* @param sb A given stream to bind the given function with.
* @param sc A given stream to bind the given function with.
* @param sd A given stream to bind the given function with.
* @param se A given stream to bind the given function with.
* @param f The function to apply to each element of this stream and the given streams.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final Stream sb, final Stream sc, final Stream sd,
final Stream se, final F>>>> f) {
return se.apply(bind(sb, sc, sd, f));
}
/**
* Binds the given function across each element of this stream and the given streams with a final
* join.
*
* @param sb A given stream to bind the given function with.
* @param sc A given stream to bind the given function with.
* @param sd A given stream to bind the given function with.
* @param se A given stream to bind the given function with.
* @param sf A given stream to bind the given function with.
* @param f The function to apply to each element of this stream and the given streams.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final Stream sb, final Stream sc, final Stream sd,
final Stream se, final Stream sf,
final F>>>>> f) {
return sf.apply(bind(sb, sc, sd, se, f));
}
/**
* Binds the given function across each element of this stream and the given streams with a final
* join.
*
* @param sb A given stream to bind the given function with.
* @param sc A given stream to bind the given function with.
* @param sd A given stream to bind the given function with.
* @param se A given stream to bind the given function with.
* @param sf A given stream to bind the given function with.
* @param sg A given stream to bind the given function with.
* @param f The function to apply to each element of this stream and the given streams.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final Stream sb, final Stream sc, final Stream sd,
final Stream se, final Stream sf, final Stream sg,
final F>>>>>> f) {
return sg.apply(bind(sb, sc, sd, se, sf, f));
}
/**
* Binds the given function across each element of this stream and the given streams with a final
* join.
*
* @param sb A given stream to bind the given function with.
* @param sc A given stream to bind the given function with.
* @param sd A given stream to bind the given function with.
* @param se A given stream to bind the given function with.
* @param sf A given stream to bind the given function with.
* @param sg A given stream to bind the given function with.
* @param sh A given stream to bind the given function with.
* @param f The function to apply to each element of this stream and the given streams.
* @return A new stream after performing the map, then final join.
*/
public final Stream bind(final Stream sb, final Stream sc, final Stream sd,
final Stream se, final Stream sf, final Stream sg,
final Stream sh,
final F>>>>>>> f) {
return sh.apply(bind(sb, sc, sd, se, sf, sg, f));
}
/**
* Performs a bind across each stream element, but ignores the element value each time.
*
* @param bs The stream to apply in the final join.
* @return A new stream after the final join.
*/
public final Stream sequence(final Stream bs) {
final F> c = constant(bs);
return bind(c);
}
/**
* Sequence through the Stream monad.
*
* @param io The IO stream to sequence.
* @return The stream of IOs after sequencing.
*/
public static Stream> sequence(final IO> io) {
return IOFunctions.runSafe(io).map(a -> IOFunctions.unit(a));
}
/**
* Sequence through the Stream monad.
*
* @param p The lazy stream to sequence.
* @return The stream of (pre-calculated) lazy values after sequencing.
*/
public static Stream> sequence(final P1> p) {
return p._1().map(a -> P.p(a));
}
/**
* Sequence through the Stream monad.
*
* @param o The optional stream to sequence.
* @return The stream of options after sequencing.
*/
public static Stream