in reply to Recursive traversal of a HoH... and paths

Given that:

  1. All leaf nodes have a value which is an empty hash;
  2. The tree can be arbitrarily deep.
then the solution can be as simple as:

sub walk { my( $v, @path ) = @_; if ( keys %$v ) { for my $k ( sort keys %$v ) { walk( $v->{$k}, @path, $k ); } } else { foo( @path ); } }

But if the tree is only two levels deep, as in your example, then you don't need a recursive solution.

We're building the house of the future together.

Replies are listed 'Best First'.
Re^2: Recursive traversal of a HoH... and paths
by bobf (Monsignor) on Oct 09, 2006 at 21:41 UTC

    Yes, the tree can be arbitrarily deep and each leaf terminates with an empty hash ref, so recursion is needed.

    Thanks for the example - it's a great starting point. If I understand it correctly, though, I'll need to change it slightly. Specifically, it looks like foo() is the criteria test, but it appears that only the keys pointing to the terminal leaves will be tested. I need to test all keys in between, too, so I'll pull that out of the else. That also means that the for loop need not be buried in the 'if' block, so I can remove the 'if' conditional. Finally, I need to track the matching paths, so I could use foo() (or similar) to do that.

    Thanks again.

      Well I don't know what you mean by 'test', but if you want to do something on every node - internal as well as leaf - then something like this:

      sub walk { my( $v, @path ) = @_; foo( @path ); if ( keys %$v ) { for my $k ( sort keys %$v ) { walk( $v->{$k}, @path, $k ); } } }

      If that something might prevent recursion below the current node, then you could have it return a "prune" boolean:

      sub walk { my( $v, @path ) = @_; foo( @path ) or return; # true means recurse if ( keys %$v ) { for my $k ( sort keys %$v ) { walk( $v->{$k}, @path, $k ); } } }

      Of course, for an even more general solution, you'd want to be able to pass in the "callback":

      sub walk { my( $cb, $v, @path ) = @_; $cb->( @path ) or return; # true means recurse if ( keys %$v ) { for my $k ( sort keys %$v ) { walk( $cb, $v->{$k}, @path, $k ); } } }

      No doubt someone else will propose an iterator-based solution...

      We're building the house of the future together.