
fj.data.Stream Maven / Gradle / Ivy
package fj.data;
import fj.Equal;
import fj.F0;
import fj.F3;
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.*;
import static fj.Bottom.error;
import static fj.Function.*;
import static fj.P.p;
import static fj.P.p2;
import static fj.P.weakMemo;
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. Equivalent to {@link #uncons}.
*
* @deprecated As of release 4.5, use {@link #uncons}
*
* @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.
*/
@Deprecated
public final B stream(final B nil, final F>, B>> cons) {
return uncons(nil, 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 uncons(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 foldRight(uncurryF2(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 isEmpty() ? b : f.f(head(), P.lazy(() -> 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 foldRight1(final F> f, final B b) {
return foldRight1(uncurryF2(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 isEmpty() ? b : f.f(head(), tail()._1().foldRight1(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) {
return foldLeft(uncurryF2(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 F2 f, final B b) {
B x = b;
for (Stream xs = this; !xs.isEmpty(); xs = xs.tail()._1())
x = f.f(x, xs.head());
return x;
}
/**
* 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) {
if (isEmpty())
throw error("Undefined: foldLeft1 on empty list");
return tail()._1().foldLeft(f, head());
}
/**
* 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) {
return foldLeft1(uncurryF2(f));
}
/**
* 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 F0> as) {
return isEmpty() ? P.lazy(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(), () -> 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()), () -> 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(), () -> 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(), () -> 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(), () -> 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(), 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(IOFunctions::unit);
}
/**
* 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 F0> p) {
return p.f().map(P::p);
}
/**
* Sequence through the Stream monad.
*
* @param o The optional stream to sequence.
* @return The stream of options after sequencing.
*/
public static Stream