No such thing as a small change PerlMonks

### compare arrays numbers

by nicholaspr (Novice)
 on Feb 16, 2010 at 18:33 UTC Need Help??
nicholaspr has asked for the wisdom of the Perl Monks concerning the following question:

Hi if I have two same size arrays of numbers and I want to subtract each number of an array with the corresponding counterpart in the other array...Can you do this without for loops??

Replies are listed 'Best First'.
Re: compare arrays numbers
by ikegami (Pope) on Feb 16, 2010 at 18:52 UTC

To do something to each element of an array, you need a loop over each element of the array. I don't know what's special about for loops that they need to be avoided. Maybe if you told us that, we could help you better.

I wouldn't use a for loop anyway. I'd use a counting loop. The syntax of a counting loop is much simpler than that of a for loop, it's faster do to the much smaller number of opcodes, and it also uses a constant amount of memory. (Mostly for the first reason. The other two are bonuses.)

Counting loop (an optimised form of foreach loop):

\$a[\$_] -= \$b[\$_] for 0..\$#a;

Foreach loop:

\$a[\$_] -= \$b[\$_] for (),0..\$#a;

Using map like a foreach loop:

map { \$a[\$_] -= \$b[\$_] } 0..\$#a;
while loop:
my \$i = @a; \$a[\$i] -= \$b[\$i] while \$i--;

Goto loop:

my \$i = @a; goto CHECK; BODY: \$a[\$i] -= \$b[\$i]; CHECK: goto BODY if \$i--;

Recursion loop:

sub subarray { my (\$a, \$b) = @_; local *_helper = sub { my \$i = \$_[0]; return if !\$i--; \$a->[\$i] -= \$b->[\$i]; _helper(\$i); }; _helper(0+@\$a); } subarray(\@a, \@b);

Recursion loop, take 2:

sub subarray { my (\$a, \$b) = @_; local *_helper = sub { return if !@_; \$_[0] -= \$_[-1]; shift; pop; &_helper; }; _helper(@\$a, reverse(@\$b)); } subarray(\@a, \@b);

pairwise loop:

use List::MoreUtils qw( pairwise ); @a = pairwise { \$a - \$b } @a, @b;

You seem to be editing your post continuously, I hope by the time I submit this the snippet in question won't be outdated... although you seem to be adding to your examples, not modifying (apparently you're working on 1,001 ways to write a loop)

In example #2:

\$a[\$_] -= \$b[\$_] for (),0..\$#a; # ^-- ... why?
What is up with the stray ()? Just a typo, or are you doing something else here (I thought you might be somehow forcing list context, but 0..\$#a is a list anyway).

Without the spare empty list, the expression evaluates equivalently in my perl (5.10.0).

\$a[\$_] -= \$b[\$_] for 0..\$#a;
is optimised into a counting loop (O(1) memory).
\$a[\$_] -= \$b[\$_] for (),0..\$#a;
is not optimised (O(N) memory).

but 0..\$#a is a list anyway

".." is usually the range operator, but not in the first example. In fact, it's not an operator at all there. It's just a separator like the semicolons in for(;;).

\$ perl -MO=Concise,-exec -e'1 for (),\$x..\$y' 1 <0> enter 2 <;> nextstate(main 1 -e:1) v:{ 3 <;> nextstate(main 1 -e:1) v:{ 4 <0> pushmark sM 5 <|> range(other->6)[t3] lK/1 \ 6 <#> gvsv[*y] s | 7 <1> flop lKM | Range op called goto 8 | to produce a list. f <#> gvsv[*x] s | g <1> flip[t4] lK / 8 <#> gv[*_] s 9 <{> enteriter(next->a last->d redo->a) lK/8 b <0> iter s c <|> and(other->a) vK/1 a <0> unstack v goto b d <2> leaveloop vK/2 e <@> leave[1 ref] vKP/REFC -e syntax OK \$ perl -MO=Concise,-exec -e'1 for \$x..\$y' 1 <0> enter 2 <;> nextstate(main 1 -e:1) v:{ 3 <;> nextstate(main 1 -e:1) v:{ 4 <0> pushmark s 5 <#> gvsv[*x] s \ Args for enteriter/S 6 <#> gvsv[*y] s / 7 <#> gv[*_] s 8 <{> enteriter(next->9 last->c redo->9) lKS/8 a <0> iter s b <|> and(other->9) vK/1 9 <0> unstack v goto a c <2> leaveloop vK/2 d <@> leave[1 ref] vKP/REFC -e syntax OK
Re: compare arrays numbers
by Perlbotics (Chancellor) on Feb 16, 2010 at 18:52 UTC

You can do it without for(each) but not without a loop (effectively). The example below uses implicit loop of map (preserves both input arrays).

use strict; my @a = qw(10 20 30); my @b = qw( 1 2 3); # without 'for' (implicit loop) my @diff = map { \$a[\$_] - \$b[\$_] } 0..\$#a; print "@diff\n"; # 9 18 27 # well, without a loop, but probably not useful in the general case... + ;-) print join(" ", \$a[0]-\$b[0], \$a[1]-\$b[1], \$a[2]-\$b[2]), "\n";
Re: compare arrays numbers
by zwon (Abbot) on Feb 16, 2010 at 18:41 UTC
@res = map { \$_ - shift(@arr2) } @arr1;
Note that @arr2 will be empty after that. You can use:
my \$i = 0; @res = map { \$_ - \$arr2[\$i++] } @arr1;
if you want to preserve @arr2
Re: compare arrays numbers
by GrandFather (Sage) on Feb 16, 2010 at 20:38 UTC

Why? As already shown there are many ways of hiding a loop, but unless we know why you want to do this strange thing it's not clear that any of the subterfuges mentioned above will be of any use to you.

If you are concerned with efficiency, don't be (this is Perl and it ain't built for that) or use XS (a module written in C) to perform the time critical part. An existing module such as PDL may be able to do the heavy lifting for you.

True laziness is hard work
Re: compare arrays numbers
by biohisham (Priest) on Feb 16, 2010 at 19:05 UTC
An ascending or descending index accessing of each of the arrays using a while loop..
use strict; my @array1=qw(1 2 3 4); my @array2=qw(4 3 2 1); #ascending my \$i =0; while(\$i<= \$#array1){ print \$array1[\$i] - \$array2[\$i],"\n"; \$i++; }
#Descending my \$i = \$#array1+1; #or # my \$i = @array1; print \$array1[\$i] -\$array2[\$i],"\n" while \$i--;

Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind.

Create A New User
Node Status?
node history
Node Type: perlquestion [id://823525]
Approved by ww
Front-paged by ww
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (6)
As of 2017-06-24 09:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
How many monitors do you use while coding?

Results (557 votes). Check out past polls.