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.
Perl has some utility module in the core you can use, such as List::Util.
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.
Thanks for the comment, Hao, you are of course right, I pondered grep briefly, but that is indeed a more elegant solution. I have another problem I'm currently working on, but you've inspired me to consider how Perl 5/5i/6 stack up on that approach so I'll make that my next blog article.
:)
As for the syntax-highlighting, as per my Update, Ovid suggested that I needed 4-spaces before each line of code (which I had) and from that I worked out that I needed to be using the Markdown editor not the Rich text editor. The syntax highlighting for the results block is a bit annoying, but I decided I'd live with it and sort out how to do an actual <pre> block in the future.
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.)
time shows:
Compared to your first example above, my local timings with perl 5.16:
That brings it down to about 10x slower instead of 75x.
My code was mangled. Trying again with slightly more compact formatting:
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 :(