Can I pull the next element from within a Perl foreach loop?

20,365

Solution 1

Must you use a for loop? Copy the original and 'consume' it with shift:

use strict;
use warnings;

my @original = 'a' .. 'z';    # Original
my @array = @original;        # Copy

while (my $token = shift @array) {

    shift @array if $token =~ /[nr]/; # consumes the next element
    print $token;
}

# prints 'abcdefghijklmnpqrtuvwxyz' ('s' and 'o' are missing)

Solution 2

Not with a foreach loop. You can use a C-style for loop:

for (my $i = 0; $i <= $#tokens; ++$i) {
  local $_ = $tokens[$i];
  if (/foo/){
     next;
  }
  if (/bar/) {
    my $nextToken = $tokens[++$i];
    # do something
    next;
  }
}

You could also use something like Array::Iterator. I'll leave that version as an exercise for the reader. :-)

Solution 3

Starting with Perl 5.12, each is now more flexible by also working on arrays:

use 5.012;
use warnings;

my @tokens = 'a' .. 'z';

while (my ($i, $val) = each @tokens) {
    if ($val =~ m/[aeiou]/) {
        ($i, $val) = each @tokens;   # get next token after a vowel
        print $val;
    }
}

# => bfjpv


One caveat with each, remember the iterator is global and is not reset if you break out of a loop.

For eg:

while (my ($i, $val) = each @tokens) {
    print $val;
    last if $i == 12;
}

# => abcdefghijklm

my ($i, $val) = each @tokens;
say "Now at => $val ($i)";         # Now at => n (13)

So use keys or values to manually reset the iterator:

keys @tokens;                      # resets iterator
($i, $val) = each @tokens;
say "Now at => $val ($i)";         # Now at => a (0)

Solution 4

    my $arr = [0..9];

    foreach ( 1 ..  scalar @{$arr} ) {

           my $curr = shift @{$arr};

           my $next = shift @{$arr};

           unshift @{$arr} , $next;

           print "CURRENT :: $curr :: NEXT :: $next \n";
    }
Share:
20,365
hansvb
Author by

hansvb

Updated on August 27, 2020

Comments

  • hansvb
    hansvb almost 4 years

    Can I do something like the following in Perl?

    foreach (@tokens) {
         if (/foo/){
           # simple case, I can act on the current token alone
           # do something
           next;
        }
        if (/bar/) {
           # now I need the next token, too
           # I want to read/consume it, advancing the iterator, so that
           # the next loop iteration will not also see it
           my $nextToken = .....
           # do something
           next;
        }
    
    }
    

    Update: I need this in Perl, but for curiosity's sake: Do other languages have a neat syntax for that?

  • hansvb
    hansvb about 14 years
    Nice. In my case, destroying original is not even a problem, so I can do without the copy.
  • zdim
    zdim over 7 years
    Whoa! I didn't know -- this will come handy. Thank you :)
  • Ilan Kleiman
    Ilan Kleiman over 3 years
    Best solution here.