Spidy has asked for the wisdom of the Perl Monks concerning the following question:

I have 2 arrays, which are both exactly the same length. They're storing numbers, and I want to loop backwards through them, from the highest number to the lowest, and from the last value to the first in the case of the second array. Does anyone know how I'd do this?

Thanks,
Spidy
  • Comment on Looping backwards through two arrays at once?

Replies are listed 'Best First'.
Re: Looping backwards through two arrays at once?
by ikegami (Patriarch) on Nov 04, 2005 at 17:04 UTC

    "Perl-style" for loop:

    for my $i (reverse 0..$#a) { print("$a[$i], $b[$i]\n"); }

    "C-style" for loop:

    for (my $i=@a; $i--; ) { print("$a[$i], $b[$i]\n"); }

    Update: Fixed crossed wires.

      ikegami,
      Your suggestion of a C style for loop as an alternative feels odd to me. I would have written it as a while loop as it feels more natural.
      my $i = @array; while ( $i-- ) { # ... }

      Cheers - L~R

        I prefer for over while because

        • the index is scoped to the loop, and
        • I find it more readable since the bounds of the loop are on a single line.

      umm....don't you mean for my $i (reverse 0 .. $#a) on the first example?

      Update: Already corrected it, it seems :).

      "Cogito cogito ergo cogito sum - I think that I think, therefore I think that I am." Ambrose Bierce

      From the belly I would have said that
      for my $i (reverse 0..$#a)
      would cast the list into existance, but as it seems that isn't the case (Perl 5.8).


      holli, /regexed monk/
Re: Looping backwards through two arrays at once?
by ikegami (Patriarch) on Nov 04, 2005 at 17:31 UTC

    A note on performance:

    Two optimizations are needed for "Perl-style" for my $i (reverse 0..$#a) to be as fast as the "C-style" for loop: 1) The elimination of reverse by processing the list backwards, and 2) the treatment of 0..$#a as an interator instead of flattening it. Not all versions of perl implement both of these implementations. As such, it's slower in some versions. (Update: 5.8.6 added (1). (2) still missing when reverse is used. c.f. Re^4: the '..' operator and decreasing values)

    This is perl, v5.6.1 built for MSWin32-x86-multi-thread Rate ickyb0d ickyb0d_ perl_for diotalevi while_loop c +_style_for ickyb0d 2516/s -- -8% -44% -48% -52% + -52% ickyb0d_ 2726/s 8% -- -40% -43% -48% + -48% perl_for 4507/s 79% 65% -- -6% -14% + -14% diotalevi 4794/s 91% 76% 6% -- -9% + -9% while_loop 5261/s 109% 93% 17% 10% -- + -0% c_style_for 5261/s 109% 93% 17% 10% 0% + --

    Still, the Perl-style for loop is not much slower (14% slower, maybe less for smaller arrays) than the C-style for loop even in "old" version 5.6.1.

    Update: Added diotalevi's solution.

      I wasn't aware of for ( reverse N .. M ) being optimized in *any* case. Because of that, I'd normally write that without the reverse() and use the number as an offset from the end. In the loop, treating the offset as a negative number works to get the "from the end" effect. This buys the iterator space savings of for(N..M) back.

      for my $offset ( 0 .. $#ary_a ) { foobar( $ary_a[ -$offset ], $ary_b[ -$offset ] ); }
        for my $offset ( 0 .. $#ary_a ) { foobar( $ary_a[ -$offset ], $ary_b[ -$offset ] ); }

        This is the reply I was going to give but you're suffering from an off-by-one error. It should be:

        for my $offset (1..@ary_a) { foobar( $ary_a[ -$offset ], $ary_b[ -$offset ] ); }
Re: Looping backwards through two arrays at once?
by ickyb0d (Monk) on Nov 04, 2005 at 17:08 UTC
    #since they're the same length... for(0..$#array1) { print $array1[$#array1 - $_]; print $array2[$#array2 - $_]; }
    hope this helps.

    UPDATE: Yeah, sorry, fixed 'em :).
      If you want to do it this way, shouldn't it be

      for(0..$#array1) { print $array1[$#array1 - $_]; print $array2[$#array2 - $_]; }

      EDIT: Oops, you fixed the array index. But you still have your $# reversed on two lines. :)