Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

compare arrays numbers

by nicholaspr (Novice)
on Feb 16, 2010 at 18:33 UTC ( #823525=perlquestion: print w/replies, xml ) 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;

    Update: Added more alternatives :)

      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 (Canon) 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--;
    UPDATED: Comments added...

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

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (7)
As of 2017-03-28 16:29 GMT
Find Nodes?
    Voting Booth?
    Should Pluto Get Its Planethood Back?

    Results (335 votes). Check out past polls.