Re: Multiplying together the numers in an array
by BrowserUk (Patriarch) on Apr 09, 2004 at 01:07 UTC
|
use List::Util qw[ reduce ];
my @a = 2..6;
print reduce{ $a*=$b } @a;
720
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
| [reply] [d/l] |
|
|
Is that *= intended as an optimization? I'd expect just *.
| [reply] |
|
|
It wasn't intended at all. I started out with the postfix for loop and then though, reduce would avoid the temp var and modified the line, leaving the *= behind.
That said, it does appear to run approximately 20% quicker which might come in handy some day.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
| [reply] [d/l] |
|
|
$total = $total * $_;
It is a holdover from C and is actually pretty useful as shorthand. | [reply] [d/l] |
|
|
Re: Multiplying together the numers in an array
by tilly (Archbishop) on Apr 09, 2004 at 02:31 UTC
|
Here is a stupid but concise way for your amusement:
print eval join "*", @numbers;
| [reply] [d/l] |
|
|
@a= (1/3, 3);
$prod1= eval join "*", @a;
$prod2= 1; $prod2*= $_ for @a;
print $prod1,$/, $prod2,$/;
outputs
0.999999999999999
1
I'm not trying to attack you, but this can be a trap at
other times when you store floating-point numbers as text.
I learned in when I wrote the obfu Fun with duff's device and AUTOLOAD.
Also look at what happens if the numbers
are object with stringification. Look:
use Math::Complex; @a= (1+i, 2); $prod1= eval join "*", @a; $prod2= 1;
+ $prod2*= $_ for @a; print $prod1,$/, $prod2,$/;
output is:
1+2i
2+2i
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
| [reply] |
Re: Multiplying together the numers in an array
by davido (Cardinal) on Apr 09, 2004 at 01:08 UTC
|
Your method is fine. Here's another solution which is quite similar to yours, except that it use a "modifier" type for loop and the *= operator.
my @numbers = (2, 3, 4, 5);
my $total=1;
$total *= $_ for @numbers;
print "$total\n";
| [reply] [d/l] |
Re: Multiplying together the numers in an array
by tachyon (Chancellor) on Apr 09, 2004 at 04:18 UTC
|
#!/usr/bin/perl
use Inline 'C';
@ary = 1..10;
print multiply( \@ary );
__DATA__
__C__
double multiply (SV * terms)
{
I32 numterms = 0;
double res = 1.0;
int i;
if ((!SvROK(terms))
|| (SvTYPE(SvRV(terms)) != SVt_PVAV)
|| ((numterms = av_len((AV *)SvRV(terms))) < 0))
{
return 0;
}
for (i = 0; i <= numterms; i++) {
res *= SvNV(* av_fetch((AV *)SvRV(terms), i, 0));
}
return res;
}
| [reply] [d/l] |
Re: Multiplying together the numers in an array
by eXile (Priest) on Apr 09, 2004 at 06:26 UTC
|
Nice post to get myself some practice in benchmarking. Here's my try:
#!/usr/bin/perl
use Benchmark qw(cmpthese);
use List::Util qw(reduce);
use Inline 'C';
cmpthese (-5,
{
'initial' => sub {
my @numbers = (2,3,4,5);
my $total = 1;
for(@numbers){
$total = ($total * $_);
}
},
'listutil' => sub {
my @numbers = (2,3,4,5);
reduce{ $a*=$b } @numbers;
},
'eval' => sub {
my @numbers = (2,3,4,5);
eval join "*", @numbers;
},
'*=' => sub {
my @numbers = (2,3,4,5);
my $total=1;
$total *= $_ for @numbers;
},
'reducing' => sub {
my @numbers = (2,3,4,5);
$numbers[0] *= pop @numbers while ( @numbers > 1 );
},
'inline' => sub {
my @numbers = (2,3,4,5);
multiply( \@numbers );
}
});
__DATA__
__C__
double multiply (SV * terms)
{
I32 numterms = 0;
double res = 1.0;
int i;
if ((!SvROK(terms))
|| (SvTYPE(SvRV(terms)) != SVt_PVAV)
|| ((numterms = av_len((AV *)SvRV(terms))) < 0))
{
return 0;
}
for (i = 0; i <= numterms; i++) {
res *= SvNV(* av_fetch((AV *)SvRV(terms), i, 0));
}
return res;
}
the results:
Rate eval reducing initial *= listutil inline
eval 21683/s -- -84% -84% -85% -85% -89%
reducing 134819/s 522% -- -4% -6% -7% -31%
initial 139854/s 545% 4% -- -3% -4% -28%
*= 144019/s 564% 7% 3% -- -1% -26%
listutil 145100/s 569% 8% 4% 1% -- -25%
inline 194405/s 797% 44% 39% 35% 34% --
If I'm doing something wrong here, please correct me. | [reply] [d/l] [select] |
|
|
FWIW, here are a couple of variations on *= :
#!/usr/bin/perl -w
use strict;
use Benchmark qw(cmpthese);
cmpthese(
-5,
{
'*=' => sub {
my @numbers = (2, 3, 4, 5);
my $total = 1;
$total *= $_ for @numbers;
},
'pop' => sub {
my @numbers = (2, 3, 4, 5);
my $total = pop @numbers;
$total *= $_ for @numbers;
},
'shift' => sub {
my @numbers = (2, 3, 4, 5);
my $total = shift @numbers;
$total *= $_ for @numbers;
}
}
);
Rate *= pop shift
*= 167004/s -- -7% -12%
pop 180190/s 8% -- -5%
shift 188837/s 13% 5% --
| [reply] [d/l] |
|
|
#!/usr/bin/perl
use Benchmark qw(cmpthese);
use List::Util qw(reduce);
cmpthese (-5,
{
'initial' => sub {
my @numbers = (2,3,4,5);
my $total = 1;
for(@numbers){
$total = ($total * $_);
}
},
'listutil' => sub {
my @numbers = (2,3,4,5);
reduce{ $a*=$b } @numbers;
},
'eval' => sub {
my @numbers = (2,3,4,5);
eval join "*", @numbers;
},
'*=' => sub {
my @numbers = (2,3,4,5);
my $total=1;
$total *= $_ for @numbers;
},
'map' => sub {
my @numbers = (2,3,4,5);
my $total=1;
map { $total *= $_ } @numbers;
},
'reducing' => sub {
my @numbers = (2,3,4,5);
$numbers[0] *= pop @numbers while ( @numbers > 1 );
},
});
And got the followin results on two runs.
C:\test>perl reduce.pl
Rate eval listutil initial *= map reducin
+g
eval 19089/s -- -86% -90% -91% -93% -93
+%
listutil 136513/s 615% -- -27% -39% -47% -51
+%
initial 187039/s 880% 37% -- -16% -27% -33
+%
*= 223842/s 1073% 64% 20% -- -13% -20
+%
map 256001/s 1241% 88% 37% 14% -- -8
+%
reducing 278189/s 1357% 104% 49% 24% 9% -
+-
C:\test>perl reduce.pl
Rate eval listutil initial *= reducing ma
+p
eval 19656/s -- -87% -92% -92% -93% -94
+%
listutil 150598/s 666% -- -35% -39% -47% -51
+%
initial 232290/s 1082% 54% -- -6% -19% -24
+%
*= 247322/s 1158% 64% 6% -- -14% -20
+%
reducing 286024/s 1355% 90% 23% 16% -- -7
+%
map 307455/s 1464% 104% 32% 24% 7% -
+-
C:\test>
It greatly surpised me how much slower the listutil function was on my machine. Are there different implementations of it for different platforms that would cause this? Or is the small number of array elements makeing everything else meaningless?
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
Re: Multiplying together the numers in an array
by Zaxo (Archbishop) on Apr 09, 2004 at 01:15 UTC
|
That's pretty much how it's done. It can look like less work with *= and modifier notation, but that's just sugar,
my @numbers = (2,3,4,5);
my $product = 1;
$product *= $_ for @numbers;
print $product;
It's possible to do an elegant recursive implementation, but it costs in performance.
| [reply] [d/l] |
Re: Multiplying together the numers in an array
by runrig (Abbot) on Apr 09, 2004 at 02:20 UTC
|
Besides the previously mentioned List::Util::reduce method, there's: @numbers = (1..5);
print product(@numbers),"\n";
sub product {
my $result = shift;
$result *= $_ for @_;
$result;
}
| [reply] [d/l] |
Re: Multiplying together the numers in an array
by graff (Chancellor) on Apr 09, 2004 at 03:20 UTC
|
Since you have the array already, and you don't like having an extra variable around, you could multiply the first element times each of the subsequent ones -- here's one way ("multiplication is commutative"):
@ary = (2..6);
$ary[0] *= pop @ary while ( @ary > 1 );
print $ary[0], $/;
| [reply] [d/l] |
Re: Multiplying together the numers in an array
by tall_man (Parson) on Apr 09, 2004 at 19:38 UTC
|
Another idea, especially if the arrays are large and you need to do many operations on them, is the module PDL.
use strict;
use PDL;
my $numbers = pdl (2,3,4,5);
my $total = prod($numbers);
print $total,"\n";
| [reply] [d/l] |
Re: Multiplying together the numers in an array
by Anonymous Monk on Apr 09, 2004 at 04:49 UTC
|
Just a thought, but depending on how you are populating the array, do the calculations at that time.
Ryan | [reply] |
Re: Multiplying together the numers in an array
by Anonymous Monk on Apr 09, 2004 at 03:55 UTC
|
perl -le'print eval join"*",qw/2 3 4/' | [reply] |
Re: Multiplying together the numers in an array
by Cody Pendant (Prior) on Apr 11, 2004 at 07:31 UTC
|
Thank you all for your contributions, it was very interesting.
($_='kkvvttuubbooppuuiiffssqqffssmmiibbddllffss')
=~y~b-v~a-z~s; print
| [reply] |