Могу ли я извлечь следующий элемент из цикла Perl foreach?

Могу ли я сделать что-то вроде следующего в 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;
    }

}

Обновление: мне нужно это в Perl, но ради любопытства: есть ли в других языках удобный синтаксис для этого?


person Thilo    schedule 05.06.2010    source источник


Ответы (5)


Обязательно ли использовать цикл for? Скопируйте оригинал и используйте его с помощью 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)
person Zaid    schedule 05.06.2010
comment
Хороший. В моем случае уничтожить оригинал даже не проблема, так что я могу обойтись и без копии. - person Thilo; 05.06.2010

Не с петлей foreach. Вы можете использовать цикл for в стиле C:

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

Вы также можете использовать что-то вроде Array::Iterator. Я оставлю эту версию в качестве упражнения для читателя. :-)

person cjm    schedule 05.06.2010

Начиная с Perl 5.12, каждый из них стал более гибким также работая с массивами:

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


Одно предостережение с each: помните, что итератор является глобальным и не сбрасывается, если вы выходите из петли.

Например:

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)

Поэтому используйте keys или values для ручного сброса итератора:

keys @tokens;                      # resets iterator
($i, $val) = each @tokens;
say "Now at => $val ($i)";         # Now at => a (0)
person draegtun    schedule 06.06.2010
comment
Вау! Я не знал - это пригодится. Спасибо :) - person zdim; 07.12.2016

Мне нравится ответ Zaids, но он терпит неудачу, если встречает нулевой элемент в массиве, поэтому...

while (@array) {

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

это не остановится, пока @array не станет пустым.

person Matt    schedule 07.06.2013

person    schedule
comment
Лучшее решение здесь. - person Ilan Kleiman; 30.09.2020