/** * Puts the elements of the tree into a Stream, in pre-order. * * @return The elements of the tree in pre-order. */ public Stream<A> flatten() { final F2<Tree<A>, P1<Stream<A>>, Stream<A>> squish = new F2<Tree<A>, P1<Stream<A>>, Stream<A>>() { public Stream<A> f(final Tree<A> t, final P1<Stream<A>> xs) { return cons( t.root(), t.subForest() .map( Stream.<Tree<A>, Stream<A>>foldRight() .f(F2Functions.curry(this)) .f(xs._1()))); } }; return squish.f(this, P.p(Stream.nil())); }
/** * Creates a new tree given a root and a (potentially infinite) subforest. * * @param root The root element of the tree. * @param forest A stream of the tree's subtrees. * @return A newly sprouted tree. */ public static <A> Tree<A> node(final A root, final Stream<Tree<A>> forest) { return new Tree<>(root, P.p(forest)); }
/** * Folds a Tree<A> into a Tree<B> by applying the function f from the bottom of the Tree to the * top * * @param t A tree to fold from the bottom to the top. * @param f A function transforming the current node and a stream of already transformed nodes * (its children) into a new node * @return The folded tree */ public static <A, B> Tree<B> bottomUp(Tree<A> t, final F<P2<A, Stream<B>>, B> f) { final F<Tree<A>, Tree<B>> recursiveCall = a -> bottomUp(a, f); final Stream<Tree<B>> tbs = t.subForest()._1().map(recursiveCall); return node(f.f(P.p(t.root(), tbs.map(Tree.getRoot()))), tbs); }
/** * Applies the given function to all subtrees of this tree, returning a tree of the results * (comonad pattern). * * @param f A function to bind across all the subtrees of this tree. * @return A new tree, with the results of applying the given function to each subtree of this * tree. The result of applying the function to the entire tree is the root label, and the * results of applying to the root's children are labels of the root's subforest, etc. */ public <B> Tree<B> cobind(final F<Tree<A>, B> f) { return unfoldTree((Tree<A> t) -> P.p(f.f(t), t.subForest())).f(this); }