All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.data.Tree Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

The newest version!
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).
 */
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(this.curry()).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 f.zipTreeM().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); } }