Good Perl code is the best form of evangelism

| 4 Comments

Here in the Perl echo chamber, there's a lot of talk about advocacy and getting people to use Perl and whether Perl is more popular in the job market than PHP, but I think most of that is just wind in sails. We talk and talk and talk but when it gets down to it, people don't use Perl because it's bigger in the job market, or because of a well-argued thread on Slashdot. People use Perl because the power of Perl makes their lives easier.

Back on the 17th, I posted that Perl would be allowed in the Microsoft Scripting Games. Now, I'm no friend of the "scripting" moniker, but I was glad it was allowed in and thought maybe there would be some good visibility for the power of Perl.

Then I saw the results.

Event 2 in the competition was to read in a text file of comma-separated values of ice skating scores, drop the high & low scores from each skater, and show the top three skaters and their average scores. Trivial for Perl, right? The solution that Microsoft posted, however, was effectively a VBScript script translated to a Perl program (by their own admission).

Here's the solution they posted.

%dictionary = ();

open (SkaterScores, "C:\\Scripts\\Skaters.txt");
@arrSkaters = <SkaterScores>;
close (SkaterScores);

foreach $strSkater (@arrSkaters) 
    {
        $intTotal = 0;
        @arrScores = split(",",$strSkater);
        
        $strName = @arrScores[0];
        @arrScoresOnly = (@arrScores[1],@arrScores[2],
                @arrScores[3],@arrScores[4],
                @arrScores[5],@arrScores[6],@arrScores[7]);

        @arrScoresOnly = sort({$a <=> $b} @arrScoresOnly);

        $intTotal = @arrScoresOnly[1] + @arrScoresOnly[2] + 
                @arrScoresOnly[3] + @arrScoresOnly[4] + 
                @arrScoresOnly[5];
        
        $dblAverage = $intTotal / 5;
        $dictionary{$strName} = $dblAverage;
    }

$i = 0;

foreach (sort {$dictionary{$b} <=> $dictionary{$a} } 
                     keys %dictionary)
    { 
        $i++;
        print "$_, $dictionary{$_}\n"; 
        if ($i == 3) {exit}
    }

Just dreadful. Among the atrocities: Stringing together list elements with plus signs to get a sum; Hardcoded array indexes; Hungarian notation prefixes of variables, in addition to the Perl sigils; breaking out of a loop after three records are shown, rather than starting with a list of the top three. It's just not good idiomatic Perl code.

Perl's arrays and hashes are powerful, and underused here. Perl arrays are treated like C arrays.

In a few minutes, I had put together the following program which is shorter and clearer because it takes advantages of Perl's strengths.

use warnings;
use strict;

my %averages;

open( my $SCORES, '<', 'c:/scripts/skaters.txt' )
    or die "Can't open score file: $!\n";
while ( <$SCORES> ) {
    chomp;
    my ($name,@scores) = split ',';
    @scores = sort @scores;

    # Drop high & low scores
    pop @scores;
    shift @scores;

    my $total;
    $total += $_ for @scores;
    $averages{$name} = $total/scalar @scores;
}
close $SCORES;

my @names_by_score = 
    sort {$averages{$b} <=> $averages{$a}} keys %averages;
for my $name ( @names_by_score[0..2] ) {
    print "$name: $averages{$name}\n";
}

(I'll admit I haven't tested it against their sample data because their sample data is only available in a self-extracting .EXE, which is a bummer for us non-Windows folks, no?)

After I wrote my solution, I noticed that they have an Experts Page that includes links to solutions offered by Jan Dubois of ActiveState. No surprise, Jan's solution is exemplary Perl, and I'm glad that Microsoft provides it. My frothing subsided a bit after discovering this.

Rather than arguing with non-Perl people about which language is better, let's show them. Even more, let's show the people who think that Perl is only a scripting language, only for web sites, or can't be deciphered that Perl will make their lives easier if they'd only open their Swiss Army knives beyond the first blade.

4 Comments

The self extracting ZIP behaves like a regular ZIP file on notWindows so you can use unzip to extract it.

This reminds me of that shitty paper that was out a few years ago by Brian Kernighan and Chris Van Wyk about the relative performance of various interpreted languages.

They took the same programs in several different languages and benchmarked them against each other. But they didn't know Perl. And rather than get advice from someone who did know Perl, they wrote awk programs and ran them through a2p and benchmarked the result.

It was really inexcusable.

Not only is the official solution written by someone who doesn’t know how to write idiomatic Perl, it’s written by someone who barely knows how to write Perl at all – else they’d at least use the right sigils.

I mean, “@arrScoresOnly[1] + @arrScoresOnly[2] + ...”? Only the greenest of novices would write this. That’s fine for novices (who are, after all, new and still learning); not so fine for an “official solution” by a company like Microsoft.

Not sure if 'core' was a requirement. If not, this line:

$total += $_ for @scores;

could have been replaced with

use List::Util qw(sum);
$total = sum @scores;

It's really a trade-off. A lot of folks don't like/understand $_ and expression modifiers nearly as easily as they do 'sum'.

In the scheme of things, however, this is a small flurish on your code. Thanks for suggesting that poorly-written Perl code is not a good public face, and that well-written, practical Perl is a lot better way to get converts than lots of academic infighting.

Don't listen to what folks say.
See what they do.

Do good perl.

Leave a comment

Job hunting for programmers


Land the Tech Job You Love, Andy Lester's guide to job hunting for programmers and other technical professionals, is available in PDF, ePub and .mobi formats, all DRM-free, as well as good old-fashioned paper.