whatever123
whatever123

Reputation: 179

Tree traversal in Java with Generic classes

To be precise, I am trying to flatten a tree and I am stuck on trying to get the values of private attributes in a generic class using a generic function.

I have attached the classes to show how the tree is structured exactly. But it's looks something like this:

  /|\
 1 | 6
  /|\
 5 4 9

I am going to paste my attempt at the end. First, let me introduce the classes:

Triple simply stores three values of the same type.

public class Triple<V> {
    private final V l, m, r;
    public Triple(V l, V m, V r) {
        this.l = l;
        this.m = m;
        this.r = r;
    }
    public V left()   { return l; }
    public V middle() { return m; }
    public V right()  { return r; }
}

Straightforward interface:

public interface Function<P, R> {
     R apply(P p);
}

Now, for a tricky class. This one is simply a type that stores one of EitherOr of two types of value, but not both.

public class EitherOr<A,B> {
    // Constructs a left-type EitherOr
    public static <A> EitherOr left(A a) {
         return new EitherOr(a, null);
    }
    // Constructs a right-type EitherOr
    public static <B> EitherOr right(B b) {
         return new EitherOr(null, b);
    }
    private final A a;
    private final B b;

    private EitherOr(A a, B b) {
         this.a = a; this.b = b;
    }

    public<T> T ifLeft(Function<A,T> f) {
        return f.apply(a);
    }

public<T> T ifRight(Function<B,T> f) {
        return f.apply(b);
    }

    public boolean isLeft() {
        return b == null;
    }
}

I know this is getting long, but bear with me. This class implements the tree structure.

public interface Tree<T> {
    EitherOr<T, Triple<Tree<T>>> get();

    static final class Leaf<T> implements Tree<T> {
           public static <T> Leaf<T> leaf (T value) {
                return new Leaf<T>(value);
           }

           private final T t;

           public Leaf(T t) { this.t = t; }

           @Override
           public EitherOr<T, Triple<Tree<T>>> get() {
                 return EitherOr.left(t);
           }
    }

    static final class Node<T> implements Tree<T> {
         public static <T> Tree<T> tree (T left, T middle, T right) {
             return new Node<T>(Leaf.leaf(left), Leaf.leaf(middle), Leaf.leaf(right));
     }

         private final Triple<Tree<T>> branches;

         public Node(Tree<T> left, Tree<T> middle, Tree<T> right) {
               this.branches = new Triple<Tree<T>>(left, middle, right);
         }

         @Override
         public EitherOr<T, Triple<Tree<T>>> get() {
                 return EitherOr.right(branches);
         }
    }
}

Alright. Here is my idea for flattening:

public class MyFlattenTree<T> implements FlattenTree<T> {
    public List<T> flattenInOrder(Tree<T> tree) {
          List<T> list = new ArrayList<T>();
          EitherOr<T, Triple<Tree<T>>> EitherOr;
          EitherOr = tree.get();
          // it is a leaf
          if (EitherOr.isLeft()) {
               // This is where the problem lies
               // I don't how to get the value using a function f
               list.add((T) EitherOr.ifLeft(f));
               return list;
          }
          else {
               // basically recursively go through the tree somehow
          }
          return null;
    }
}

As I said, I am stuck with trying to retreive the value in the EitherOr class using the Function interface. I am thinking of implementing the Function interface and write a function for "apply" that just gets the value, but I am not sure how to do that. Any help would be appreciated. Thanks!

Upvotes: 4

Views: 704

Answers (1)

ccjmne
ccjmne

Reputation: 9618

So, here is your flattenInOrder method:

public List<T> flattenInOrder(final Tree<T> tree) {
    final EitherOr<T, Triple<Tree<T>>> EitherOr = tree.get();
    if (EitherOr.isLeft()) {
        return Collections.singletonList(EitherOr.ifLeft(this.ifLeftFunction));
    }

    return EitherOr.ifRight(this.ifRightFunction);
}

Quite simple, assuming that:

  • ifLeftFunction yields a single element (since EitherOr<T, Triple<Tree<T>>> has a single T elem' if it s "left")

... and:

  • ifRightFunction yields a collection of elements (since EitherOr<T, Triple<Tree<T>>> has a list of T elems' if it is "right")

Let's look into these functions now:

ifLeftFunction is... basic. I want to extract a T from... a T.

final Function<T, T> ifLeftFunction = new Function<T, T>() {

    @Override
    public T apply(final T t) {
        return t;
    }
};

ifRightFunction is slightly more complex: it has to be recursive and collect all Ts from the Tree it's browsing:

final Function<Triple<Tree<T>>, List<T>> ifRightFunction = new Function<Triple<Tree<T>>, List<T>>() {

    @Override
    public List<T> apply(final Triple<Tree<T>> t) {
        final List<T> res = new ArrayList<>();
        res.addAll(MyFlattenTree.this.flattenInOrder(t.left()));
        res.addAll(MyFlattenTree.this.flattenInOrder(t.middle()));
        res.addAll(MyFlattenTree.this.flattenInOrder(t.right()));
        return res;
    }
};

And... you're done!


Sample working code:

public class MyFlattenTree<T> {

    private final Function<Triple<Tree<T>>, List<T>> ifRightFunction = new Function<Triple<Tree<T>>, List<T>>() {

        @Override
        public List<T> apply(final Triple<Tree<T>> t) {
            final List<T> res = new ArrayList<>();
            res.addAll(MyFlattenTree.this.flattenInOrder(t.left()));
            res.addAll(MyFlattenTree.this.flattenInOrder(t.middle()));
            res.addAll(MyFlattenTree.this.flattenInOrder(t.right()));
            return res;
        }
    };

    private final Function<T, T> ifLeftFunction = new Function<T, T>() {

        @Override
        public T apply(final T t) {
            return t;
        }
    };

    public static void main(final String[] args) {
        final Tree<String> tree = new Node<>(new Leaf<>("1"), new Node<>(new Leaf<>("5"), new Leaf<>("4"), new Leaf<>("9")), new Leaf<>("6"));
        System.out.println(new MyFlattenTree<String>().flattenInOrder(tree));
    }

    public List<T> flattenInOrder(final Tree<T> tree) {
        final EitherOr<T, Triple<Tree<T>>> EitherOr = tree.get();
        if (EitherOr.isLeft()) {
            return Collections.singletonList(EitherOr.ifLeft(this.ifLeftFunction));
        }

        return EitherOr.ifRight(this.ifRightFunction);
    }
}

Note that I'm creating the exact Tree you're featuring as an example in your question in the main method here:

public static void main(final String[] args) {
    final Tree<String> tree = new Node<>(new Leaf<>("1"), new Node<>(new Leaf<>("5"), new Leaf<>("4"), new Leaf<>("9")), new Leaf<>("6"));
    System.out.println(new MyFlattenTree<String>().flattenInOrder(tree));
}

Output: [1, 5, 4, 9, 6]


Cheers ;)

Upvotes: 3

Related Questions