Advanced Challenge 3...
A couple of different approaches here: one is
to maintain a list of excluded candidates, and then ignore them during
recounts. The other is to actually delete them from the ballot and then
recount. Both solutions work: the first uses junctions; the other uses the
"is rw" trait on a for loop. So both are of some interest.
First: the junctional approach...
my @ballots = (
[< A B C D >],
[< B D C A >],
[< C D B A >],
[< B A C D >],
[< A D C B >],
);
my $winner;
my @excluded;
while ! $winner {
my %tallies;
for @ballots -> @ballot {
if @ballot.first: { $_ ne all(@excluded) } -> $chosen {
%tallies{ $chosen } ++;
}
}
my $total = [+] %tallies.values;
if %tallies.pairs.first: { .value > $total/2 } -> $kv {
$winner = $kv.key;
}
else {
my $losing_tally = min %tallies.values;
for %tallies.pairs.grep: { .value == $losing_tally } -> $kv {
my $loser = $kv.key;
say "exclude $loser";
@excluded.push: $loser;
}
}
}
say "WINNER IS: $winner";
That wasn't too hard. Note the "pointy-block" used with the "if" statement -- this syntax isn't limited to "for" loops!
The other way is almost identical:
my $winner;
while ! $winner {
my %tallies;
for @ballots.grep: {.elems} -> @ballot {
%tallies{ @ballot[0] } ++;
}
my $total = [+] %tallies.values;
if %tallies.pairs.first: { .value > $total/2 } -> $kv {
$winner = $kv.key;
}
else {
my $losing_tally = min %tallies.values;
for %tallies.pairs.grep: { .value == $losing_tally } -> $kv {
my $loser = $kv.key;
say "exclude $loser";
for @ballots -> @ballot is rw {
@ballot = @ballot.grep: { $_ ne $loser }
}
}
}
}
say "WINNER IS: $winner";
Again, no problems.
Two things were "slightly harder than they should be".
First, the "inverse lookup" of a hash key from its value seems a little
cumbersome. Sure, there's probably no getting round the need to iterate
over all the values: but wouldn't it be nice the perl took care of that
for me. In the first solution I did this iteration as a "for" loop;
in the latter I used ".first" and ".grep" methods
The other niggle is that the "dot-assign" concept doesn't seem to
exist yet. Theoretically I should have been able to write:
@ballot.=grep: { $_ ne $loser }
to do the ballot modification for exclusion. Hopefully that'll be
implemented in the fullness of time.