9

Here is the code...

use strict;
use warnings;

my @array= (1,2,3,4,5);
my $scalar= 5;

@array= $scalar*@array;

print @array;

Need something that can perform similar function with little code. Thanks!

masterial
  • 2,166
  • 9
  • 33
  • 48
  • 1
    For manipulation larger arrays you might want to look at PDL http://pdl.perl.org – MkV Apr 15 '11 at 03:41

6 Answers6

10

Use foreach.

foreach my $x (@array) { $x = $x * $scalar; }
RC.
  • 27,409
  • 9
  • 73
  • 93
  • 1
    +1. The `foreach` approach is faster than `map` for large lists. – Alex Reynolds Apr 15 '11 at 00:42
  • @Alex - that's probably true in an array context, but the subject of endless debate in a void context (like my second version). It's apparently platform- and version-specific. Also, map can (in principle) be parallelized by the system, while foreach generally can't, so there's at least the possibility that map can execute faster than foreach. – Ted Hopp Apr 15 '11 at 02:01
  • Or just `$_ *= $scalar for @array`. – Sean Apr 15 '11 at 04:21
  • @Alex @Ted do you guys have any relevant links? :) – wprl Apr 15 '11 at 04:40
  • I'll post an answer with some code, which you can run locally to confirm or disprove my comment. – Alex Reynolds Apr 15 '11 at 05:06
  • 1
    @Ted Hopp, parallel processing of `map` is a feature that may make it's way into Perl 6, but it will likely never find it's way into Perl 5. Far too much code relies on ordered iteration of map statements. Perl 5 is far to side effect heavy for `perl` to recognize what map bodies are totally side effect free. – Ven'Tatsu Apr 15 '11 at 15:44
  • 1
    the speed of map depends on perl version. Newer versions of Perl recognise when map iscalled in void context and do not store the results of the map and then discard the results at the end, which older Perl did. – MkV Apr 15 '11 at 16:23
  • 1
    @SoloBold - just google "perl map vs foreach" to get lots of good discussions. The ones at perlmonks.org tend to be well-informed and concrete. – Ted Hopp Apr 15 '11 at 19:53
  • @Ted Ah, thanks for the pointer. For some reason I didn't think it would be as easy as Googling that, but for instance: http://www.perlmonks.org/?node_id=809482 – wprl Apr 18 '11 at 01:26
10

You can try this:

@array = map { $_ * $scalar } @array;

or more simply:

map { $_ *= $scalar } @array;
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • 3
    You shouldn't modify $_ in a map block – MkV Apr 15 '11 at 03:29
  • 1
    @MkV - Why not? From the [perldoc page for map](http://perldoc.perl.org/functions/map.html): "Note that $_ is an alias to the list value, so it can be used to modify the elements of the LIST." It's a common idiom for transforming all elements of an array. – Ted Hopp Apr 15 '11 at 04:07
  • "While this is useful and supported, it can cause bizarre results if the elements of LIST are not variables. Using a regular foreach loop for this purpose would be clearer in most cases." – MkV Apr 15 '11 at 16:13
  • @MkV, But the element of the list are variables. That said, I'd use `$_ *= $scalar for @array;` myself. – ikegami Apr 15 '11 at 18:46
  • @ikegami - I would probably go with your suggestion as well, after all is said and done. Not for performance reasons, but because it strikes me as more readable than anything else. :) – Ted Hopp Apr 15 '11 at 19:55
  • The 2nd sentence I quoted seems to not be referring to the situation where the LIST is not variables, but the use of map to modify in-place in general. – MkV Apr 16 '11 at 08:49
  • Just a note on something I found confusing here; if you want to use `map` in say a `printf` simply to show each value changed by a constant, you actually do not want to modify `$_` - it's enough to do something like `printf "%6.2f "x$#MyArray."\n", map { (120 - $_) } @MyArray;`... Cheers! – sdaau Sep 21 '11 at 23:09
7

Howabout this:

foreach(@array)
{ $_ *= $scalar }

As you see, you can modify the array in-place as it's traversed.

jwd
  • 10,837
  • 3
  • 43
  • 67
  • 5
    you might as well do $_ *= $scalar for @array if you are going to rely on an implicit $_ – MkV Apr 15 '11 at 03:39
6

I don't know the scope of your need. IFF you are doing numerical data manipulation, the Perl Data Language (PDL) takes an array of numerical data, creates a "piddle" object from it and overloads mathematical operations to "vectorize" their operation. This is a very efficient system for doing numerical processing. Anyway here is an example:

#!/usr/bin/perl

use strict;
use warnings;

use PDL;

my $pdl_array = pdl([1,1,2,3,5,8]);
print 2*$pdl_array;

__END__
gives:
[2 2 4 6 10 16]
Joel Berger
  • 20,180
  • 5
  • 49
  • 104
5

This comment is for SoloBold.

Here is a test of the map approach:

#!/usr/bin/perl                                                                                                                                                                                                          

use strict;
use warnings;
use Benchmark;

my @array = ();
push(@array, (1) x 1000000);
my $scalar = 5;

my $startTime = new Benchmark();

@array = map { $_ * $scalar } @array;

my $stopTime = new Benchmark();

print STDOUT "runtime: ".timestr(timediff($stopTime, $startTime), 'all')." sec\n";

Here is a test of the foreach approach:

#!/usr/bin/perl                                                                                                                                                                                                          

use strict;
use warnings;
use Benchmark;

my @array = ();
push(@array, (1) x 1000000);
my $scalar = 5;

my $startTime = new Benchmark();

foreach my $x (@array) { $x = $x * $scalar; }

my $stopTime = new Benchmark();

print STDOUT "runtime: ".timestr(timediff($stopTime, $startTime), 'all')." sec\n";

Here is the system I'm running on:

bash-3.2$ perl --version
This is perl, v5.8.8 built for darwin-2level
...
bash-3.2$ uname -a
Darwin Sounder.local 10.7.0 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386

Here were results from one test:

bash-3.2$ ./test.map.pl
runtime:  4 wallclock secs ( 0.41 usr  0.70 sys +  0.00 cusr  0.00 csys =  1.11 CPU) sec
bash-3.2$ ./test.foreach.pl
runtime:  0 wallclock secs ( 0.13 usr  0.00 sys +  0.00 cusr  0.00 csys =  0.13 CPU) sec

These times are fairly reproducible on the same machine, and the results are somewhat repeatable on a dual-core Linux box:

[areynolds@fiddlehead ~]$ perl --version
This is perl, v5.8.8 built for x86_64-linux-thread-multi
...
[areynolds@fiddlehead ~]$ uname -a
Linux fiddlehead.example.com 2.6.18-194.17.1.el5 #1 SMP Mon Sep 20 07:12:06 EDT 2010 x86_64 GNU/Linux
[areynolds@fiddlehead ~]$ ./test.map.pl
runtime:  0 wallclock secs ( 0.28 usr  0.05 sys +  0.00 cusr  0.00 csys =  0.33 CPU) sec
[areynolds@fiddlehead ~]$ ./test.foreach.pl
runtime:  0 wallclock secs ( 0.09 usr  0.00 sys +  0.00 cusr  0.00 csys =  0.09 CPU) sec

The ratio of performance on the OS X box is 8.53x slower for map versus foreach. On the Linux box, 3.67x slower for the same.

My Linux box is dual-core and has a slightly faster cores than my single-core OS X laptop.

EDIT

I updated Perl from v5.8.8 to v5.12.3 on my OS X box and got a considerable speed boost, but map still performed worse than foreach:

sounder:~ alexreynolds$ perl --version
This is perl 5, version 12, subversion 3 (v5.12.3) built for darwin-multi-2level
...
sounder:~ alexreynolds$ ./test.map.pl
runtime:  0 wallclock secs ( 0.45 usr  0.08 sys +  0.00 cusr  0.00 csys =  0.53 CPU) sec
sounder:~ alexreynolds$ ./test.foreach.pl
runtime:  1 wallclock secs ( 0.18 usr  0.00 sys +  0.00 cusr  0.00 csys =  0.18 CPU) sec

This goes from 8.53x worse to 2.94x worse. A fairly substantial improvement.

The Linux box performed slightly worse with upgrading its Perl installation to v5.12.2:

[areynolds@basquiat bin]$ perl --version    
This is perl 5, version 12, subversion 2 (v5.12.2) built for x86_64-linux-thread-multi
...
[areynolds@basquiat bin]$ /home/areynolds/test.map.pl
runtime:  1 wallclock secs ( 0.29 usr  0.07 sys +  0.00 cusr  0.00 csys =  0.36 CPU) sec
[areynolds@basquiat bin]$ /home/areynolds/test.foreach.pl
runtime:  0 wallclock secs ( 0.08 usr  0.00 sys +  0.00 cusr  0.00 csys =  0.08 CPU) sec

This goes from 3.67x worse to 4.5x worse — not so good! It might not always pay to upgrade, just for the heck of it.

Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345
1

it seem unfortunate to me that Larry didn't allow

$scalar operator (list)

or

(list) operator $scalar

Sure map or loops can do it, but the syntax is so much cleaner like above.

Also (list) operator (list)

makes sense too if the 2 are equal length.

Surprised Larry didn't allow these, just saying.. I guess in this case there were (n-1) ways to do it.

Like

my @a = 'n' . (1..5); my @a = 2 * (1..5);

or even my @a = 2 * @b;