No such thing as a small change PerlMonks

### Re^3: traversing a hash looking for path?

by Limbic~Region (Chancellor)
 on Apr 12, 2006 at 14:29 UTC ( #542847=note: print w/replies, xml ) Need Help??

in reply to Re^2: traversing a hash looking for path?
in thread traversing a hash looking for path?

roboticus,
Thank you. I have a real hard time thinking recursively for some reason. There are some things that I would want to change. For instance, letting the call stack be handled automatically by the recursion and not relying on lexicals visible at the package level. Here is my take on satisfying those two changes:
```my %net = (...); # shortened for brevity sake

print find_path('1.2.3.4', '1.2.3.6', \%net);
sub find_path {
my (\$beg, \$end, \$net, \$seen, \$stop, \$node, \$path) = @_;

if (! defined \$stop) {
(\$node, \$path) = (\$beg, \$beg);
\$stop->{\$_} = 1 for @{\$net->{\$end}};
}
return \$path if \$node eq \$end;
return "\$path=>\$end" if \$stop->{\$node};
return undef if \$seen->{\$node}++;

for (@{\$net->{\$node}}) {
my \$found = find_path(\$beg, \$end, \$net, \$seen, \$stop, \$_, "\$pa
+th=>\$_");
return \$found if \$found;
}
}
I liked the iterative solution better because I could alter the search process by adjusting the shift/unshift and push/pop. I thought recursive solutions were supposed to be more elegant. Perhaps it is just because I don't know what I am doing.

Cheers - L~R

Replies are listed 'Best First'.
Re^4: traversing a hash looking for path?
by roboticus (Chancellor) on Apr 12, 2006 at 23:31 UTC
L~R:

For some problems, recursion can be more elegant. For some other problems, it's just another way to do the job. For the remaining few problems, it's just a harder way to do things.

In fact, for this problem, it may be that recursion could be more elegant. My implementation, however, makes no such claim! Especially as you've made a few improvements! 8^)

(In Perl, I'm not very skilled (yet), so you'd be agog at how much time I spent debugging it before posting. I was happy enough to get it done! Once there, I was afraid to touch it in persuit of elegance. We need to get a good golfer in here for that...)

Regarding the altering of the search process: I'd imagine that you may be able to get there with a recursive solution, as well--not that one comes to mind for this task!

I've been meaning to beat Lisp into my brain, but after learning a few programming languages, I've adopted the policy: No learning a new language until I get paid for it! It's too bad I can't convince my boss to let me write anything in Lisp..... So, I'm trying to learn some Lisp-like Perl idioms, but I've got quite a way to go!

--roboticus

roboticus,
The problem with a recursive BFS is that you have to pass forward your nodes. You want to first check all your nodes of a distance of 1, then 2, etc until you find your destination.

It turns out that the only real difference between this and my iterative solution is that you have 2 stacks. The first is the current work load (nodes at current distance) and the second is you future work load (nodes at the next distance) which you pass forward.

While the find_path() sub that follows is quite elegant, the remainder of the code isn't.

This isn't the result of recursive BFS being all that ugly or intention obfu. I was just experimenting. It can be brought back into a single sub that looks quite reasonable (which is what I started with). Since working backwards to clean code is easier, I thought you might enjoy seeing the things I played with.

Update: Since others besides roboticus might be interested, I am providing the relatively clean version as well.

Cheers - L~R

Hi limbic~region,

I would have done it something like this:

```my %net = ( ... );
my \$start = '1.2.3.4';
my \$end = '1.2.3.6';
print find_pathr( \%net, \$end, \$start ) . "\n";

sub find_pathr {
my ( \$net, \$end, @list ) = @_;
my \$curr;
do {
\$curr = shift @list;
} until ( ! @list or exists \$\$net{ \$curr } );
return "No path!" unless exists \$\$net{\$curr};
return \$end if \$curr eq \$end;
unshift @list, @{ delete \$\$net{ \$curr } };
return "\$curr->" . find_pathr( \$net, \$end, @list );
}

Now this quick implementation isn't perfect. It doesn't pretend to find the most efficient path, it finds the first path based on the order of the elements of %net. It is destructive to %net. I don't like the do ... until loop.

But I do think it is an elegant solution, IMNSHO ;)

Update: This is a depth-first search, not breadth-first as is limbic~region's.

Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://542847]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (4)
As of 2022-01-19 23:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
In 2022, my preferred method to securely store passwords is:

Results (56 votes). Check out past polls.

Notices?