See WardNumber for the problem definition.
See WardNumberInManyProgrammingLanguages for many other implementations.
use strict; my (%partners, %wardnumber, $p); sub visit { my ($cnt, @closer_folks) = @_; my @newfolks; foreach my $i (@closer_folks) { foreach my $j (@{$partners{$i}}) { unless ($wardnumber{$j}) { $wardnumber{$j} = $cnt; push @newfolks, $j; } } } visit (++$cnt, @newfolks) if @newfolks; } sub pair { push @{$partners{$_[0]}}, $_[1]; } open FS, "data.txt"; while (<FS>) { if (/\s*(.*)\s*,\s*(.*)\s*/) { pair($1,$2); pair ($2,$1); } } visit(1, 'ward'); foreach $p (sort keys %wardnumber) { print "$p $wardnumber{$p}\n"}
Here's another one by JohnDouglasPorter
my %g; # the graph. # parsing is not interesting. for ( "al bob", "bob cal", "cal dave", "dave ed ", "cal fred", "al ward", "dave ward", ) { my( $x, $y ) = split; $g{$x}{$y}++; $g{$y}{$x}++; } sub graph_distance # BFS { my( $goal, $n ) = @_; my @try_paths = ( [$n] ); while ( @try_paths ) { my @path = @{ shift @try_paths }; $path[0] eq $goal and shift @path, return @path; my %path_nodes; @path_nodes{ @path } = (); my @next = grep { ! exists $path_nodes{$_} } keys %{ $g{$path[0]} }; @next and push @try_paths, map { [ $_, @path ] } @next; } die "No path from $n to $goal.\n"; } my $n = 'ward'; my $wwnum = graph_distance( $n ); print "Ward number for $n is $wwnum\n";
And another by TonyBowden?:
#!/usr/bin/perl -w use strict; use Class::Struct Programmer => { wardno => '$', pairs => '@' }; use List::Util 'min'; my @pairs = qw/al bob bob cal cal dave dave ed cal fred al ward dave ward/; sub Programmer::add_pair { my ($self, $prog) = @_; $self->pairs([ @{$self->pairs}, $prog ]); } my %prog; while (my ($x, $y) = map $prog{$_} ||= Programmer->new, splice @pairs, 0, 2) { $x->add_pair($y); $y->add_pair($x); } $prog{ward}->wardno(0); while (my @need = grep !defined $_->wardno, values %prog) { foreach my $prog (@need) { my @known = grep defined, map $_->wardno, @{ $prog->pairs } or next; $prog->wardno(1 + min @known); } } sub ward_number { $prog{+shift}->wardno }
TMTOWTDI - here's one by AristotlePagaltzis:
This one calculates everyone's WardNumber with regard to the target programmer at once. The edge_distance() function here can actually be used on any graph where all edges are bidirectional.
#!/usr/bin/perl use strict; use warnings; # idiomatic Perl sub flatten_hashrefs { return map { keys %{$_} } @_; } # very common idiom sub list_contains { my ($element, @list) = @_; return scalar grep $_ eq $element, @list; } # hash of hashes that stores the graph my %connections_of; sub connected_to { return flatten_hashrefs( @connections_of{@_} ); } sub edge_distance { my ($initial_node, $target_node) = @_; my %seen; my $wnum = 0; my @occupied_node = ( $initial_node ); do { # nodes we occupy have been seen ++$seen{$_} for @occupied_node; # occupy connected nodes, except those we've seen before @occupied_node = grep { !$seen{$_} } connected_to( @occupied_node ); # undefined edge distance if no route to target return if not @occupied_node; ++$wnum; } until( list_contains( $target_node, @occupied_node ) ); return $wnum; } # read graph data while(<DATA>) { my ($from, $to) = split; ++$connections_of{$from}{$to}; ++$connections_of{$to}{$from}; } my $TARGET = shift( @ARGV ) || 'Ward'; for( keys %connections_of ) { my $wnum = edge_distance( $_, $TARGET ); print defined( $wnum ) ? "$wnum edges between $_ and $TARGET\n" : "No route from $_ to $TARGET\n"; } __END__ Al Ward Joe John Al Bob Bob Cal Cal Dave Joe Peter Ray Peter Dave Ed Cal Fred Dave Ward
I think I would use Graph from CPAN: http://search.cpan.org/dist/Graph/