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
Recent versions of List::Util include a
sum0
function which returns a nicer result when, say,$max
is 2.A PDL response might be:
Plain old Perl looks pretty good here:
for (1..$max) {
$sum += $_ unless $_ % 5 || $_ % 3
}
print "Total: $sum\n";
This little baby is about two orders of magnitude faster than any of the previously posted solutions...
Ain´t that wrong because of short-circuiting?
I´d say you could write it as
This might make it slightly easier to see what's going on in my solution:
We find the number
$n
of multiples of 3 below$max
. Then do the triangular number thing ($n * ($n+1) / 2
) and multiply that by 3 to scale it up correctly. Then we do the same for 5. Then, because we've double counted multiples of 15, we do the same for 15 and subtract it.Overall this solution is O(1) rather than O(n) because we don't need to loop through an increasingly long list of numbers. We can calculate
$max=10_000_000
in roughly the same time it takes to calculate$max=10
.What version of Perl 6 are you using for your local timings?
Just did the Perl 6 example on my Macbook Air and got a very different time! I have a feeling the poster is using the JVM build without understanding that it has about ~8 seconds start up time regardless of what you do, even something like `perl6 -e ''`
$ perl6 --version
This is perl6 version 2013.09 built on parrot 5.5.0 revision 0
$ sysctl -a | grep machdep.cpu.brand.string
machdep.cpu.brand_string: Intel(R) Core(TM) i5-2557M CPU @ 1.70GHz
$ time perl6 stuff.p6
My multiples are: 3 5 6 9 10 12 15 18 20 21 24 25 27 30 33 35 36 ... *snip* ... 993 995 996 999
Total: 233168
real 0m1.289s
user 0m1.037s
sys 0m0.220s
My times for the Perl 5 version for comparison:
real 0m0.064s
user 0m0.010s
sys 0m0.015s
So my Mac has a slower time for the Perl 5 code so I'd imagine the OP would have a slightly better time than 1s for the Perl 6 code running in the latest R* parrot build?
Do note that you can use @*ARGS instead of providing a sub MAIN; though the latter will give you a usage message when you don't supply the max and if you write
instead, you'll even get a helpful error message if the user writes "over nine thousand" instead of a number.Also, for interpolating arrays into strings, you can also just write
And another thing: I think "1 ..^ $max" looks prettier than "1 .. $max-1".