Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

colomon (8994)

colomon
  (email not shown publicly)

Journal of colomon (8994)

Friday December 26, 2008
09:15 PM

Third Script: Finished Version

[ #38167 ]
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";
}

Two difficulties of note here. First, I initially tried making @votes an array of arrays. I couldn't figure out any obvious way to make this work in Perl 6. Second problem is

perl6(28102) malloc: *** error for object 0x2eb5a10: double free
*** set a breakpoint in malloc_error_break to debug
Segmentation fault

after the script properly finishes.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • sub EffectiveVote ( Str $vote, Hash %skip ) {
        return $vote.split( ',' ).first( { ! %skip{$_}.defined } );
        die "No valid vote?!";
    }

    • Okay, that's definitely a big improvement over mine, and quite elegant. However, I'm wondering about return versus die 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?

      • Argh, yes. Good point.

        • Would

          sub 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.)

          • 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;
    }

    • 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 i

    • That CountRound line is nice. I've worked it into my code, I'll post another full version as a fresh post. Thanks!