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.


7 Comments

Perl has some utility module in the core you can use, such as List::Util.

use v5.14;
use List::Util qw(sum);
my $max = (shift || 1000) - 1;
my @multiples = grep { not $_ % 5 && $_ % 3 } 1 .. $max;
say "@multiples";
say sum @multiples;

for the code highlighting in the blog, you may want to see
https://metacpan.org/release/TOBYINK-Pod-HTML

The author has an article about it, but I can not find it.

Don´t have no perl6 runtime here to verify it, but in your time comparison the Perl 6 solution actually gives a different result (98 instead of 78)?!

I tried to run the perl6 version here against a git-fresh perl6, but it died with a syntax error.

Here's a version that is more idiomatic, faster, and gives the right same answer as the perl5 examples (the difference seems to be that the perl6 example above included "20", while p5 variants excluded it.)

use v6;

sub MAIN($max = 1000) {
my @threes = 3, 6, 9 ... * >= $max;
my @fives = 5, 10, 15 ... * >= $max;

# combine the series, order, eliminate dupes, remove any trailing high elements.
my @multiples = (@threes, @fives).sort.squish.grep({$_

say "My multiples are: ", @multiples;
say "total: {[+] @multiples}";
}

time shows:

$ time perl6-p foo.p6 20
My multiples are: 3 5 6 9 10 12 15 18
total: 78

real 0m0.921s
user 0m0.780s
sys 0m0.139s

Compared to your first example above, my local timings with perl 5.16:

real	0m0.084s
user	0m0.076s
sys	0m0.008s

That brings it down to about 10x slower instead of 75x.

My code was mangled. Trying again with slightly more compact formatting:

use v6;

sub MAIN($max = 1000) {
my @threes = 3, 6, 9 ... * >= $max;
my @fives = 5, 10, 15 ... * >= $max;

my @multiples = (@threes, @fives)\
.sort.squish.grep({$_

# Print multiples and the total.
say "My multiples are: ", @multiples;

say "total: {[+] @multiples}";
}

In your P6 code you have this line of code:

unless any(@multiples) $start {

That looks like TTIAR (two terms in a row) to me (syntax error).

The code is still mangled :(

Leave a comment

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.