fj.data.Tree 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.*;
import static fj.Function.*;
import static fj.data.Stream.*;
import java.util.Collection;
import java.util.Iterator;
/**
* Provides a lazy, immutable, non-empty, multi-way tree (a rose tree).
*
* @version %build.number%
*/
public final class Tree implements Iterable {
/**
* Returns an iterator for this tree. This method exists to permit the use in a for
-each loop.
*
* @return A iterator for this tree.
*/
public Iterator iterator() {
return flatten().iterator();
}
private final A root;
private final P1>> subForest;
private Tree(final A root, final P1>> subForest) {
this.root = root;
this.subForest = subForest;
}
/**
* Creates a nullary tree.
*
* @param root The root element of the tree.
* @return A nullary tree with the root element in it.
*/
public static Tree leaf(final A root) {
return node(root, 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 Tree node(final A root, final P1>> forest) {
return new Tree(root, forest);
}
/**
* 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 Tree node(final A root, final Stream> forest) {
return new Tree(root, P.p(forest));
}
/**
* Creates a new n-ary given a root and a subforest of length n.
*
* @param root The root element of the tree.
* @param forest A list of the tree's subtrees.
* @return A newly sprouted tree.
*/
public static Tree node(final A root, final List> forest) {
return node(root, forest.toStream());
}
/**
* First-class constructor of trees.
*
* @return A function that constructs an n-ary tree given a root and a subforest or length n.
*/
public static F>>, Tree>> node() {
return curry((a, p1) -> node(a, p1));
}
/**
* Returns the root element of the tree.
*
* @return The root element of the tree.
*/
public A root() {
return root;
}
/**
* Returns a stream of the tree's subtrees.
*
* @return A stream of the tree's subtrees.
*/
public P1>> subForest() {
return subForest;
}
/**
* Provides a transformation from a tree to its root.
*
* @return A transformation from a tree to its root.
*/
public static F, A> root_() {
return a -> a.root();
}
/**
* Provides a transformation from a tree to its subforest.
*
* @return A transformation from a tree to its subforest.
*/
public static F, P1>>> subForest_() {
return a -> a.subForest();
}
/**
* Puts the elements of the tree into a Stream, in pre-order.
*
* @return The elements of the tree in pre-order.
*/
public Stream flatten() {
final F2, P1>, Stream> squish = new F2, P1>, Stream>() {
public Stream f(final Tree t, final P1> xs) {
return cons(t.root(), t.subForest().map(Stream., Stream>foldRight().f(F2Functions.curry(this)).f(xs._1())));
}
};
return squish.f(this, P.p(Stream.nil()));
}
/**
* flatten :: Tree a -> [a]
* flatten t = squish t []
* where squish (Node x ts) xs = x:Prelude.foldr squish xs ts
* Puts the elements of the tree into a Stream, in pre-order.
*
* @return The elements of the tree in pre-order.
*/
public static F, Stream> flatten_() {
return t -> t.flatten();
}
/**
* Provides a stream of the elements of the tree at each level, in level order.
*
* @return The elements of the tree at each level.
*/
public Stream> levels() {
final F>, Stream>> flatSubForests =
Stream., Tree>bind_().f(compose(P1.>>__1(), Tree.subForest_()));
final F>, Stream> roots = Stream., A>map_().f(Tree.root_());
return iterateWhile(flatSubForests, Stream.>isNotEmpty_(), single(this)).map(roots);
}
/**
* Maps the given function over this tree.
*
* @param f The function to map over this tree.
* @return The new Tree after the function has been applied to each element in this Tree.
*/
public Tree fmap(final F f) {
return node(f.f(root()), subForest().map(Stream., Tree>map_().f(Tree.fmap_().f(f))));
}
/**
* Provides a transformation to lift any function so that it maps over Trees.
*
* @return A transformation to lift any function so that it maps over Trees.
*/
public static F, F, Tree>> fmap_() {
return f -> a -> a.fmap(f);
}
/**
* Folds this tree using the given monoid.
*
* @param f A transformation from this tree's elements, to the monoid.
* @param m The monoid to fold this tree with.
* @return The result of folding the tree with the given monoid.
*/
public B foldMap(final F f, final Monoid m) {
return m.sum(f.f(root()), m.sumRight(subForest()._1().map(foldMap_(f, m)).toList()));
}
/**
* Projects an immutable collection of this tree.
*
* @return An immutable collection of this tree.
*/
public Collection toCollection() {
return flatten().toCollection();
}
/**
* Provides a function that folds a tree with the given monoid.
*
* @param f A transformation from a tree's elements to the monoid.
* @param m A monoid to fold the tree with.
* @return A function that, given a tree, folds it with the given monoid.
*/
public static F, B> foldMap_(final F f, final Monoid m) {
return t -> t.foldMap(f, m);
}
/**
* Builds a tree from a seed value.
*
* @param f A function with which to build the tree.
* @return A function which, given a seed value, yields a tree.
*/
public static F> unfoldTree(final F>>> f) {
return b -> {
final P2>> p = f.f(b);
return node(p._1(), p._2().map(Stream.>map_().f(unfoldTree(f))));
};
}
/**
* 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 Tree cobind(final F, B> f) {
return unfoldTree((Tree t) -> P.p(f.f(t), t.subForest())).f(this);
}
/**
* Expands this tree into a tree of trees, with this tree as the root label, and subtrees as the labels of
* child nodes (comonad pattern).
*
* @return A tree of trees, with this tree as its root label, and subtrees of this tree as the labels of
* its child nodes.
*/
public Tree> cojoin() {
final F, Tree> id = identity();
return cobind(id);
}
private static Stream drawSubTrees(final Show s, final Stream> ts) {
return ts.isEmpty() ? Stream.nil()
: ts.tail()._1().isEmpty() ? shift("`- ", " ", ts.head().drawTree(s)).cons("|")
: shift("+- ", "| ", ts.head().drawTree(s))
.append(drawSubTrees(s, ts.tail()._1()));
}
private static Stream shift(final String f, final String o, final Stream s) {
return Stream.repeat(o).cons(f).zipWith(s, Monoid.stringMonoid.sum());
}
private Stream drawTree(final Show s) {
return drawSubTrees(s, subForest._1()).cons(s.showS(root));
}
@Override
public boolean equals(Object other) {
return Equal.equals0(Tree.class, this, other, () -> Equal.treeEqual(Equal.anyEqual()));
}
@Override
public int hashCode() {
return Hash.treeHash(Hash.anyHash()).hash(this);
}
@Override
public String toString() {
return Show.treeShow(Show.anyShow()).showS(this);
}
/**
* Draws a 2-dimensional representation of a tree.
*
* @param s A show instance for the elements of the tree.
* @return a String showing this tree in two dimensions.
*/
public String draw(final Show s) {
return Monoid.stringMonoid.join(drawTree(s), "\n");
}
/**
* Provides a show instance that draws a 2-dimensional representation of a tree.
*
* @param s A show instance for the elements of the tree.
* @return a show instance that draws a 2-dimensional representation of a tree.
*/
public static Show> show2D(final Show s) {
return Show.showS(tree -> tree.draw(s));
}
/**
* Zips this tree with another, using the given function. The resulting tree is the structural intersection
* of the two trees.
*
* @param bs A tree to zip this tree with.
* @param f A function with which to zip together the two trees.
* @return A new tree of the results of applying the given function over this tree and the given tree, position-wise.
*/
public Tree zipWith(final Tree bs, final F2 f) {
return F2Functions.zipTreeM(f).f(this, bs);
}
/**
* Zips this tree with another, using the given function. The resulting tree is the structural intersection
* of the two trees.
*
* @param bs A tree to zip this tree with.
* @param f A function with which to zip together the two trees.
* @return A new tree of the results of applying the given function over this tree and the given tree, position-wise.
*/
public Tree zipWith(final Tree bs, final F> f) {
return zipWith(bs, uncurryF2(f));
}
/**
* Folds a Tree into a Tree 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 Tree bottomUp(Tree t, final F>, B> f) {
final F, Tree> recursiveCall = new F, Tree>() {
@Override public Tree f(Tree a) {
return bottomUp(a, f);
}
};
final Stream> tbs = t.subForest()._1().map(recursiveCall);
return Tree.node(f.f(P.p(t.root(), tbs.map(Tree. getRoot()))), tbs);
}
/**
* @return a function getting the root of a Tree
*/
private static F, A> getRoot() {
return a -> a.root();
}
}