How do you implement a recursive walker of a tree with a lazy range?

bearophile bearophileHUGS at lycos.com
Wed Oct 30 05:55:40 PDT 2013


Chris Cain:

>     InputRange!Tree walk()
>     {
>         return inputRangeObject(chain(
>             [this],
>             children.map!(a=>a.walk())().joiner()));
>     }


I have used your nice idea to create another partial D solution 
for this task:
http://rosettacode.org/wiki/Tree_traversal#D


import std.stdio, std.algorithm, std.range, std.conv, std.string;

struct Tree(T) {
     T value;
     Tree* left, right;
}

alias VisitRange(T) = InputRange!(const Tree!T);

VisitRange!T preOrder(T)(in Tree!T* t) {
     enum self = mixin("&" ~ __FUNCTION__.split(".").back);
     if (t == null)
         return typeof(return).init.takeNone.inputRangeObject;
     return [*t]
            .chain([t.left, t.right]
                   .filter!(t => t != null)
                   .map!(a => self(a))
                   .joiner)
            .inputRangeObject;
}

VisitRange!T inOrder(T)(in Tree!T* t) {
     enum self = mixin("&" ~ __FUNCTION__.split(".").back);
     if (t == null)
         return typeof(return).init.takeNone.inputRangeObject;
     return [t.left]
            .filter!(t => t != null)
            .map!(a => self(a))
            .joiner
            .chain([*t])
            .chain([t.right]
                   .filter!(t => t != null)
                   .map!(a => self(a))
                   .joiner)
            .inputRangeObject;
}

VisitRange!T postOrder(T)(in Tree!T* t) {
     enum self = mixin("&" ~ __FUNCTION__.split(".").back);
     if (t == null)
         return typeof(return).init.takeNone.inputRangeObject;
     return [t.left, t.right]
            .filter!(t => t != null)
            .map!(a => self(a))
            .joiner
            .chain([*t])
            .inputRangeObject;
}

VisitRange!T levelOrder(T)(in Tree!T* t) {
     enum self = mixin("&" ~ __FUNCTION__.split(".").back);
     return typeof(return).init.takeNone.inputRangeObject;
     // UNFINISHED
}

void main() {
     alias N = Tree!int;
     auto tree = new N(1,
                       new N(2,
                             new N(4,
                                   new N(7)),
                             new N(5)),
                       new N(3,
                             new N(6,
                                   new N(8),
                                   new N(9))));

     tree.preOrder.map!(t => t.value).writeln;
     tree.inOrder.map!(t => t.value).writeln;
     tree.postOrder.map!(t => t.value).writeln;

     // Should print: [1, 2, 3, 4, 5, 6, 7, 8, 9]
     tree.levelOrder.map!(t => t.value).writeln;
}


As you see levelOrder is not finished.

I don't know if there is a simple way to return that something 
for empty input (simpler than 
typeof(return).init.takeNone.inputRangeObject). I have used it to 
avoid bugs caused by the successive map.writeln.

I don't know if there are nice ways to refactor this code to 
shorten it some.

I'd like that function pointer self to be a D built-in feature. I 
have used it because when you write such kind of code and you 
copy&paste the implementations to create a new visit function 
it's very easy to forget to update the name of the function to 
call recursively.

A related task that is not yet using this idea:
http://rosettacode.org/wiki/Same_Fringe#Haskell

Bye,
bearophile


More information about the Digitalmars-d-learn mailing list