This is the instant runoff election task. I don't feel like I had terrible problems writing it, but I feel like it's a very satisfying piece of code, either.
my $votes_file = "votes.txt";
sub EffectiveVote (Str $vote, Hash %skip) { my @vote = split ',', $vote; for @vote -> $choice { if (! %skip{$choice}.defined) { return $choice; } } die "No valid vote?!"; }
sub CountRound (Array @votes, Hash %skip) { say "{@votes.elems} votes"; my %count; my $total = 0; for @votes -> $vote { my $choice = EffectiveVote($vote, %skip); %count{$choice}++; $total++; } my %percentages; for %count.keys -> $choice { %percentages{$choice} = %count{$choice} / $total; } return %percentages; }
my @votes;
my $votes = open($votes_file); for (=$votes) -> $vote { push @votes, $vote; }
my $count = 0; my %skip; while (1) { say; say "Round {++$count}"; my %percentages = CountRound(@votes, %skip); my @ordered = sort { %percentages{$^b} <=> %percentages{$^a} }, %percentages.keys; for @ordered -> $vote { say "$vote: {%percentages{$vote}}"; } if (%percentages{@ordered[0]} > 0.5) { say; say "The winner is {@ordered[0]} with {%percentages{@ordered[0]} * 100.0}% of the vote."; exit; }
my $skip = @ordered.pop; %skip{$skip} = 1; say "Skipping $skip"; }
perl6(28102) malloc: *** error for object 0x2eb5a10: double free *** set a breakpoint in malloc_error_break to debug Segmentation faultafter the script properly finishes.
sub EffectiveVote ( Str $vote, Hash %skip ) {
return $vote.split( ',' ).first( { ! %skip{$_}.defined } );
die "No valid vote?!";
}
Re:EffectiveVote
colomon on 2008-12-27T10:05:37
Okay, that's definitely a big improvement over mine, and quite elegant. However, I'm wondering aboutreturn
versusdie
here. (And my wondering is more complicated because I cannot find documentation for.first
anywhere.)If no valid vote is found, won't the first line return undefined rather than not return?
Re:EffectiveVote
Aristotle on 2008-12-27T10:52:54
Argh, yes. Good point.
Re:EffectiveVote
colomon on 2008-12-27T13:29:00
Wouldsub EffectiveVote ( Str $vote, Hash %skip ) {
return $vote.split( ',' ).first( { ! %skip{$_}.defined } )
err die "No valid vote?!";
}do it, in theory? (In practice, I haven't gotten
err die
to work for me yet.)Re:EffectiveVote
colomon on 2008-12-27T13:33:48
Hey,sub EffectiveVote ( Str $vote, Hash %skip ) {
return $vote.split( ',' ).first( { ! %skip{$_}.defined } )
// die "No valid vote?!";
}actually compiled and it works! (I tested created a second votes file to make sure that it properly caught the condition, and it does.)
sub CountRound ( @votes, %skip ) {
my %fraction;
my $f = { EffectiveVote( $^vote, %skip ) };
for @votes.map( $f ) { %fraction{$_}++ }
for %fraction.values { $_/= @votes.elems }
return %fraction;
}
Re:CountRound
colomon on 2008-12-27T13:42:00
That's an interesting approach. On consideration, I decided it would be easier to drop the percentage thing altogether, yielding this, which is slightly wordier than yours but dead simple.sub CountRound (Array @votes, Hash %skip)
{
my %count;
for @votes -> $vote
{
%count{EffectiveVote($vote, %skip)}++;
}
return %count;
}
Unlike the others this is untested.
my @votes = do {
my $votes = open($votes_file);
=$votes
};
my $count = 0;
my %skip;
loop {
say "\nRound {++$count}";
my @ranking = CountRound( @votes, %skip ).pairs.sort({.value});
say sprintf "%s: %s",.key, .value
for @ranking;
given @ranking[0].value {
when $_ > 0.5 {
say sprintf "\nThe winner is %s with %.0f%% of the vote.",.key, .value;
exit;
}
}
my $skip = @ranking[-1].key;
%skip{ $skip } = 1;
say "Skipping $skip";
}
Re:main program
colomon on 2008-12-27T14:00:34
That CountRound line is nice. I've worked it into my code, I'll post another full version as a fresh post. Thanks!