1 module tooling.TreeRange; 2 3 import std.array, std.algorithm, std.range; 4 5 // Walk a nested (tree) structure "depth-first". Return the path from root to 6 // leaf in each step. 7 8 auto treeRange(alias isBranch, alias children, T)(T[] root) 9 { 10 struct Walker 11 { 12 this(T[] nodes) 13 { 14 nodes_ = nodes; 15 } 16 bool empty() { return nodes_.empty && stack_.empty; } 17 T[] front() { return stack_.filter!(e => !e.empty).map!(e => e.front).array ~ nodes_.front; } 18 Walker save() { return this; } 19 void popFront() 20 { 21 if (!nodes_.empty) 22 { 23 auto f = nodes_.front; 24 if (isBranch(f)) 25 { 26 stack_ ~= nodes_; 27 nodes_ = children(f); 28 } 29 else 30 { 31 nodes_ = nodes_[1 .. $]; 32 } 33 } 34 while (nodes_.empty && !stack_.empty) 35 { 36 nodes_ = stack_.back()[1 .. $]; 37 stack_.popBack(); 38 } 39 } 40 41 T[] nodes_; 42 T[][] stack_; 43 } 44 45 return Walker(root); 46 } 47 48 unittest 49 { 50 import std.stdio, std.conv; 51 class Node 52 { 53 this(int payload, Node[] children) { 54 this.payload = payload; 55 this.children = children; 56 } 57 int payload; 58 Node[] children; 59 } 60 auto nodes = [new Node(1, [new Node(2, [new Node(3, [])]), 61 new Node(4, [new Node(5, [new Node(6, [])])]), 62 new Node(7, []) 63 ]) 64 ]; 65 66 auto r = treeRange!(t => !t.children.empty, t => t.children)(nodes); 67 foreach (e; r) { 68 writeln(e.map!(i => to!string(i.payload)).joiner("-")); 69 } 70 }