use constant SRC => 0; use constant TGT => 1; use constant NET => 2; use constant SEEN => 3; use constant STOP => 4; use constant WORK => 5; use constant PATH => 6; sub find_path { &init if ! defined $_[STOP]; return $_[PATH] if $_[PATH]; &next_hop; return $_[PATH] if $_[PATH]; goto &find_path if @{$_[WORK]}; return 'path not found'; } sub next_hop { my ($tgt, $stop, $net, @work) = (@_[TGT, STOP, NET], @{$_[WORK]}); $_[WORK] = []; for (@work) { my ($node, $path) = @{$_}{qw/key path/}; next if $_[SEEN]->{$node}++; $_[PATH] = $path, return if $node eq $tgt; $_[PATH] = "$path=>$tgt", return if $stop->{$node}; push @{$_[WORK]}, map {key => $_, path => "$path=>$_"}, @{$net->{$node}}; } } sub init { my ($src, $tgt, $net) = @_[SRC, TGT, NET]; %{$_[STOP]} = map {$_ => 1} @{$net->{$tgt}}; for (@{$net->{$src}}) { $_[PATH] = "$src=>$_", return if $_ eq $tgt; push @{$_[WORK]}, {key => $_, path => "$src=>$_"}; } } #### sub find_path { my ($beg, $end, $net, $seen, $stop, $work) = @_; if (! defined $stop) { %$stop = map {$_ => 1} @{$net->{$end}}; for (@{$net->{$beg}}) { return "$beg=>$_" if $_ eq $end; push @$work, {key => $_, path => "$beg=>$_"}; } } my $next = []; for (@$work) { my ($node, $path) = @{$_}{qw/key path/}; next if $seen->{$node}++; return $path if $node eq $end; return "$path=>$end" if $stop->{$node}; push @$next, map {key => $_, path => "$path=>$_"}, @{$net->{$node}}; } return find_path($beg, $end, $net, $seen, $stop, $next) if @$next; return 'path not found'; }