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

Greetings Most Revered Ones,

I have come across this several times already in my brief perl programming history and wonder if there is a short-cut key (or something) I'm missing. Your insight is appreciated.

What I'd like to do is access the values in each element of the array and the index...

Here are the two ideas I have... Is there a better way?
#Sample 1. $n=0; foreach $i( @array ) { #do something with $i #do something with $n $n++; } #Sample 2. foreach $i( 0..$#array ){ #do something with $array[$i] #do something with $i }

Thanks again, Brad

Replies are listed 'Best First'.
Re: array looping with foreach
by Roy Johnson (Monsignor) on Jan 18, 2008 at 16:20 UTC
    See this subthread about each for arrays (and my relatively trivial implementation).

    The ways you've listed are pretty standard for using for, with the second being somewhat preferred, IMO.

    Update: if you use method #1, put the increment in a continue block.


    Caution: Contents may have been coded under pressure.
Re: array looping with foreach
by amarquis (Curate) on Jan 18, 2008 at 16:25 UTC

    Both look fine to me, although I think I'd personally lean away from option 1. I like to have as few moving parts as possible, and an index that can potentially de-sync if I throw in loop control constructs (and not hit the increment) worries me. But, I'm bad, so many things worry me.

    I was curious to see what I've done in the past, so I took a peek at the script I've been working on this week. Interestingly, I think that this problem of "Tracking the index and the value concurrently" is a "C problem" in my mind. Each place I've had to do it, I've rolled a C-style for loop :).

Re: array looping with foreach (try Array::Each::Override)
by grinder (Bishop) on Jan 18, 2008 at 17:07 UTC

    You can install Array::Each::Override and override each. This will allow you to obtain both the index and the value of each array element.

    • another intruder with the mooring in the heart of the Perl

      I'm a little confused by the point of Array::Each::Override

      Being able to override each to use it on an array is nice and surely useful at times. But at the same time you're hurting the built in each performance on hashes (which you're likely to be doing more of in your code to start with). The values override adds nothing to working with arrays, and again hurts the built in values performance on hashes. The keys override doesn't appear to add much in the way of usefulness either again with the built in keys performance hit.

      But I'm running off topic...
Re: array looping with foreach
by NetWallah (Canon) on Jan 18, 2008 at 19:03 UTC
    Depending on how often you need to use this construct, you could build your own iterator with callback, to handle this:
    my @arr = ( 110..117) ; # Generic array Iterator --- # Calls back $cb passing it a 2-element array : the INDEX, and an alia +s to the contents at that index sub iter{ my ($cb,$aref)=@_; # $cb is the callback sub ref passed in.. my $idx=0; for(@$aref){ $cb->($idx,$_); $idx++ } }; # Call the iterator iter(sub{print "ops : @_\n"},\@arr); # Prints: #ops : 0 110 #ops : 1 111 #ops : 2 112 # ....etc ...
    Advantages:
    • Index is localized/encapsulated (and can be hardened, if necessary)
    • No temporary arrays created (Memory efficient)
    • CPU - Efficient (minimal overhead)
    • Update 1:No initialization required, No overlap or threading issues(As compared to other implementations of the "each" function)

         "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom

Re: array looping with foreach
by ikegami (Patriarch) on Jan 18, 2008 at 19:48 UTC

    The following both provides the index and makes $ele an alias to the array element as if you had done for my $ele (@array):

    for my $idx (0..$#array) { for my $ele ($array[$idx]) { ... } }
Re: array looping with foreach
by jeanluca (Deacon) on Jan 18, 2008 at 16:54 UTC
    You examples look fine to me too, but if you really want to do it differently, this seems to work too
    #!/usr/bin/perl -l use strict; use warnings; my @arr = ( 1,2,3,4,5) ; my $n = 1 ; my @new = map{ $n = 1000; ++$_ } @arr ; print "new=@new"; print "n=$n\n";
    Cheers
    LuCa
Re: array looping with foreach
by mpeg4codec (Pilgrim) on Jan 18, 2008 at 22:54 UTC
    Why not use the traditional C-style version of for as amarquis hinted?
    for (my $i = 0; $i < @array; ++$i) { # do something with $array[$i] # do something with $i }

      There is never a need in Perl to use a C style for loop to iterate over a range like that. Instead use the safer and clearer:

      for my $index (0 .. $#array) { ... }

      There are situations where a C style for loop is appropriate in Perl, but they are in fact fairly rare. About the only case that readily comes to mind is where a complicated termination test is required, and even then it is often clearer to use last with a statement modifier in the body of the loop. If multiple statements need to be executed at the end of each iteration you should use a continue block rather than pile all the statements into the iteration part of a C for loop header.


      Perl is environmentally friendly - it saves trees
      It's fun to see all the variations (map/module/each/iter) but in my mind, the C-Style loop wins (mpeg4codec++). I've always used this construction, maybe it's just because I learned C long before Perl, but it's a clear and well known construction that doesn't create extra arrays, use Module.pm, or have the (probably trivial) function call overhead of iterators. I wonder if we don't occasionally try to be too Perlish just because this is NOT C.
        I wonder if we don't occasionally try to be too Perlish just because this is NOT C.
        I thought that was the point! We try to be Perlish because this isn't language X. Perhaps our definitions of "Perlish" differ.

        Familiarity is not a good argument as not everyone comes from C. I suspect people who came from say a lisp or python background would not immediately recognize a C style for loop.

        I will admit it is likely the most efficient, but, for me, that time is lost in correcting all my stupid off by one errors.