<?xml version="1.0" encoding="windows-1252"?>
<node id="640390" title="Re: Question about recursively generated iterators" created="2007-09-21 11:52:47" updated="2007-09-21 07:52:47">
<type id="11">
note</type>
<author id="381608">
ikegami</author>
<data>
<field name="doctext">
&lt;p&gt;I tried to see if I could find a generic way of transforming a recursive function into an iterator. I found a method that's can be applied rather mechanically.

&lt;p&gt;Let's say your recursive function visits a tree in preorder. The following would be the a non-iterative solution:

&lt;c&gt;
sub visit_preorder(&amp;$) {
   my ($visitor, $node) = @_;
   my ($val, $l, $r) = @$node;
   $visitor-&gt;() for $val;
   &amp;visit_preorder($visitor, $l) if defined $l;
   &amp;visit_preorder($visitor, $r) if defined $r;
}
&lt;/c&gt;

&lt;p&gt;Change the visitor code to &lt;c&gt;return \$val;&lt;/c&gt; where &lt;c&gt;$val&lt;/c&gt; is the data to return from the iterator. &lt;c&gt;return \@val;&lt;/c&gt; is also acceptable if you wish to return more than one value.

&lt;p&gt;Break up the function where a recursive call is made. Return each block as a function as follows:

&lt;c&gt;
sub visit_preorder {
   my ($node) = @_;
   my ($val, $l, $r) = @$node;
   return (
      sub { return \$val;                                    },
      sub { return visit_preorder($l) if defined $l; return; },
      sub { return visit_preorder($r) if defined $r; return; },
   );
}
&lt;/c&gt;

&lt;p&gt;In this case, it can be simplified a little.

&lt;c&gt;
sub visit_preorder {
   my ($node) = @_;
   my ($val, $l, $r) = @$node;
   return (
      \$val,
      defined($l) ? sub { return visit_preorder($l); } : (),
      defined($r) ? sub { return visit_preorder($r); } : (),
   );
}
&lt;/c&gt;

&lt;p&gt;Now we just need an engine to drive this.

&lt;c&gt;
sub make_iter {
   my $f = shift @_;
   my @todo = $f-&gt;(@_);
   return sub {
      while (@todo) { 
         my $todo = shift @todo;
         if (ref($todo) eq 'CODE') {
            unshift @todo, $todo-&gt;();
         } elsif (ref($todo) eq 'ARRAY') {
            return @$todo;
         } else {
            return $$todo;
         }
      }
      return;
   };
}
&lt;/c&gt;

&lt;p&gt;Finally, an example caller.

&lt;c&gt;
{
   #      a
   #     / \
   #    b   e
   #   / \   \
   #  c   d   f

   my $tree = [
      'a',
      [
         'b',
         [
            'c',
            undef,
            undef,
         ],
         [
            'd',
            undef,
            undef,
         ], 
      ],
      [
         'e',
         undef,
         [
            'f',
            undef,
            undef,
         ], 
      ], 
   ];

   my $iter = make_iter(\&amp;visit_preorder, $tree);
   while (my ($name) = $iter-&gt;()) {
      print($name);
   }
   print("\n");
}
&lt;/c&gt;
</field>
<field name="root_node">
640382</field>
<field name="parent_node">
640382</field>
</data>
</node>
