Reconsidering Exercise 1

Hao Wu provided a great comment showing how I could solve exercise one using sum from List::Util and grep. I'd considered sum but utilzed false laziness and didn't use it. I'd also considered grep, but did not immediately hit upon the elegant solution that Hao suggested and so went with a more verbose solution.

So, here are some new solutions.

Perl 5:

use v5.14;
use List::Util qw(sum);
my $max = (shift || 1000) - 1;

# For each number, collect it if it is a multiple of 3 or 5 (where % returns false)
my @multiples = grep { not $_ % 5 && $_ % 3 } 1 .. $max;

# and we're done.
say "My multiples are: @multiples";
say "Total: ", sum @multiples;

Short, elegant, more obviously correct. What's not to love?

Perl 5i:

use perl5i::2;

my $max = (shift || 1000) - 1;

# For each number, collect it if it is a multiple of 3 or 5 (where % returns false)
my @multiples = grep { not $_ % 5 && $_ % 3 } 1 .. $max;

# and we're done.
say "My multiiples are: @multiples";
say "Total: ", @multiples->sum;

Essentially idential. I could have used grep as a function on the list 1 .. $max but it's more to ugly give grep the subref it requires in that format.

Perl 6:

use v6;

sub MAIN( $max = 1000 ) {

    # For each number, collect it if it is a multiple of 3 or 5 (where %% returns true)
    my @multiples = grep { $_ %% 5 || $_ %% 3 }, 1 .. $max-1; 

    # and we're done.
    "My multiples are: {@multiples}".say;
    say "Total: ", [+] @multiples;
}

I thought that maybe a gather/take would be better than the grep here, but I'm pretty sure that's mistaken. I do like %%, that's pretty neat. "{@array}" threw me for a bit, but I did like [+] for reduce.

And approximate time comparison:

jarich@blackberry:~/polyglot$ time perl m2.pl 30
My multiples are: 3 5 6 9 10 12 15 18 20 21 24 25 27
Total: 195

real      0m0.050s
user      0m0.032s
sys       0m0.016s

jarich@blackberry:~/polyglot$ time perl5i m2.p5i 30 
My multiples are: 3 5 6 9 10 12 15 18 20 21 24 25 27
Total: 195

real      0m0.671s
user      0m0.624s
sys       0m0.040s

jarich@blackberry:~/polyglot$ time perl6 m2.p6 30
My multiples are: 3 5 6 9 10 12 15 18 20 21 24 25 27
Total: 195

real      0m8.731s
user      0m8.561s
sys       0m0.116s

Exercise 1. 3s and 5s.

My starting problem is easy. Coming from Project Euler:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

Perl 5:

use v5.14.2;
use strict;
use warnings;
use autodie;

# We want all the factors lower than this number.
my $max = (shift || 1000) - 1;

# Collect all of the multiples into a hash. (We'd use an array, but this is
# faster to making sure that we don't double up)
my %multiples;

foreach my $factor (5, 3) {
    # Find our first multiple
    my $start = $max - ( $max % $factor );

    # Now just add each factor until zero (skipping ones already added)
    while( $start > 0 ) {
        unless( $multiples{$start} ) {
            $multiples{$start}++;
        }
        $start -= $factor;
    }
}

# Print multiples and the total.
say "My multiples are: ", join " ", sort { $a <=> $b } keys %multiples;

my $total;
$total += $_ for keys %multiples;
say "total: $total";

Not much to say about that solution. I could calculate the total as I'm adding things into the hash and thus not have to walk over it at the end, I could (of course) put the magic numbers into variables, but it's a fairly standard approach. Perl 5.14.2 is what I have on my machine.

The perl5i solution is much the same:

use perl5i::2;

# We want all the factors lower than this number.
my $max = (shift || 1000) - 1;

# All of our multiples
my @multiples;

foreach my $factor (5, 3) {
    # Find our first multiple
    my $start = $max - ( $max % $factor );

    # Now just add each factor until zero (skipping ones already added)
    while( $start > 0 ) {
        unless( @multiples->first(sub {$_ $start} ) ) {
           @multiples->push($start);
        }
        $start -= $factor;
    }
}

# Print multiples and the total.
my @sorted = sort { $a <=> $b } @multiples;
say "My multiples are: ", @sorted->join(" ");

#say "My multiples are: ", @multiples->sort( sub { $[0] <=> $[1]} )->join(" ");

my $total;
$total += $_ for @multiples;
say "total: $total";

I really wanted to use the sort->join->say kind of chaining, but I think the sub for the sort block is ugly. The sub for first is pretty annoying too.

Perl 6:

use v6;

# We want all the factors lower than this number.
sub MAIN($max = 1000) {

    # All of my multiples.
    my @multiples;

    for (5, 3) -> $factor {
        # Find our first multiple
        my $start = $max - ( $max % $factor );

        # Now just add each factor until zero (skipping ones already added)
        while $start > 0  {
            unless any(@multiples) $start {
               @multiples.push($start);
            }
            $start -= $factor;
        }
    }

    # Print multiples and the total.
    say "My multiples are: ", @multiples.sort({$a <=> $b}).join(" ");

    my $total;
    $total += $_ for @multiples;
    say "total: $total";
}

It took me a little while to find MAIN, but if that's how I have to process command line arguments, so be it. The rest was pretty straight forward after some initial confusions about any() and the sort block for a while.

Time Comparisons:

jarich@blackberry:~/polyglot$ time perl multiples35.pl 20
My multiples are: 3 5 6 9 10 12 15 18
total: 78

real    0m0.117s
user    0m0.100s
sys    0m0.016s

jarich@blackberry:~/polyglot$ time perl5i multiples35.p5i 20
My multiples are: 3 5 6 9 10 12 15 18
total: 78

real    0m0.651s
user    0m0.608s
sys    0m0.040s

jarich@blackberry:~/polyglot$ time perl6 multiples35.p6 20
My multiples are: 3 5 6 9 10 12 15 18 20
total: 98
real    0m8.910s
user    0m8.685s
sys    0m0.148s

Not a real benchmark, but Perl 6 is noticably slower.

PS: Any hints on getting the syntax highlighting available in code blocks to work in blockcode in any of the available editors very gratefully appreciated.

UPDATE: Thanks to Ovid, I found to get the formatting I wanted, I needed to use the Markdown editor, and then the indentation I had.

Purpose of this blog

At this point, I don't have a Perl blog. I do Perl things, but nothing that ever seems worth blogging about. However, I've decided to improve my knowledge of Perl 5i and Perl 6, and generally practice my proramming, so I'm going to work through a bunch of easy through to difficult problems in each language. Hopefully I'll pick up the idiomatic solution ideas as I go along.

Feedback and alternate solutions are also welcome.

At some point I may repeat the problems with Python, but not yet.

Problem sets I'm planning on starting with include:

About jarich

user-pic I know Perl 5, but I could stand to know more about perl5i and perl6, so I'm going to try to solve basic to hard problems in each of the three languages, learning the idioms as I go.