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=>$_"}; } }