
fj.data.Tree Maven / Gradle / Ivy
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 Tree::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 Tree::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()));
}
/**
* {@code
* 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 Tree::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 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 = a -> bottomUp(a, f);
final Stream> tbs = t.subForest()._1().map(recursiveCall);
return 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 Tree::root;
}
public boolean isLeaf() {
return subForest._1().isEmpty();
}
public int length() {
return 1 + subForest._1().map(Tree::length).foldLeft((acc, i) -> acc + i, 0);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy