Как читать два элемента за раз в цикле Perl Perl? - PullRequest
34 голосов
/ 20 февраля 2009

Я ищу что-то вроде:

@list = qw(1 2 3 4 5 6);
foreach (@list) {
  #perl magic goes here 
  print "i: $i, j:$j\n";
}

возвращается:

i:1, j:2
i:3, j:4
i:5, j:6

В ответ на очень хорошее предложение, приведенное ниже, мне нужно указать, что этот скрипт будет работать на чужом сервере сборки, и мне не разрешено использовать какие-либо модули из CPAN. Только стандартный Perl.

Ответы [ 18 ]

38 голосов
/ 20 февраля 2009

Я считаю, что правильный способ сделать это - использовать natatime из List :: MoreUtils :

из документов:

natatime BLOCK LIST

Создает итератор массива для циклического перебора массива из кусков $n элементов. вовремя. (n за раз, понимаешь?). Пример, вероятно, лучше объяснение, которое я мог бы дать словами.

Пример:

 my @x = ('a' .. 'g');
 my $it = natatime 3, @x;
 while (my @vals = $it->())
 {
     print "@vals\n";
 }

Это печатает

a b c
d e f
g

Реализация List::MoreUtils::natatime:

sub natatime ($@)
{
    my $n = shift;
    my @list = @_;

    return sub
    {
        return splice @list, 0, $n;
    }
}
20 голосов
/ 20 февраля 2009

Я бы использовал сращивание.

my @list = qw(1 2 3 4 5 6);
while(my ($i,$j) = splice(@list,0,2)) {
  print "i: $i, j: $j\n";
}
15 голосов
/ 20 февраля 2009

Настройте некоторые тестовые данные и импортируйте say:

use Modern::Perl;
use List::AllUtils qw'zip';

my @array = zip @{['a'..'z']}, @{[1..26]} ;

Простой цикл с использованием переменной приращения.

    {
      my $i = 0;
      while(
        (my($a,$b) = @array[$i++,$i++]),
        $i <= @array # boolean test
      ){
        say "$a => $b";
      }
    }

Цикл по парам с использованием List::Pairwise (pair).

    use List::Pairwise qw'pair';

    for my $pair (pair @array){
      my($a,$b) = @$pair;

      say "$a => $b";
    }

Цикл по массиву 2 за раз, используя List::MoreUtils (natatime).

    use List::AllUtils qw'natatime';

    my $iter = natatime 2, @array;
    while( my($a,$b) = $iter->() ){
      say "$a => $b";
    }

Приведите его в хеш и переберите ключи. Полезно, если вы не заботитесь о заказе.

    {
      my %map = @array;
      for my $key (keys %map){
        my $value = $map{$key};
        say "$key => $value";
      }
    }
15 голосов
/ 20 февраля 2009

Я думаю, вы хотите сделать это по-другому. Попробуйте это:

while (scalar(@list) > 0) {
    $i = shift(@list);
    $j = shift(@list);
    print "i: $i, j:$j\n";
} 

Имейте в виду, что это уничтожит список, но это сработает для этого маленького цикла.

10 голосов
/ 20 февраля 2009

Ближайшим эквивалентом, к сожалению, является олдскул:

for(my $ix = 0; $ix <= $#list; $ix += 2) {
    my $i = $list[$ix];
    my $j = $list[$ix + 1];
    print "i: $i, j:$j\n";
}

Мне больше нравится ответ Джека М, хотя я бы написал его более сексуально: Perl:

while(@list) {
    my $i = shift @list;
    my $j = shift @list;
    print "i: $i, j:$j\n";
}
7 голосов
/ 20 февраля 2009

Если бы я мог использовать только стандартный Perl без каких-либо модулей, я бы, вероятно, переключился на цикл в стиле C, который считает 2:

for( my $i = 0; $i < @array; $i += 2 ) {
    my( $i, $j ) = @array[ $i, $i+1 ];
    ...
    }

Однако, если вы хотите что-то необычное из одного из модулей, которые вы не можете использовать, вы можете просто добавить этот модуль в свой код. Если вы можете написать код, вы можете использовать модули. Возможно, вам просто нужно включить модуль со всем кодом, который вы поставляете, когда вы правильно установили @INC. Это основная идея inc :: Module :: Install и PAR .

Я трачу много времени на работу с системой сборки, которая создает собственный репозиторий CPAN, устанавливает его зависимости из своего частного CPAN, а затем тестирует код. Наличие фермы сборки не исключает использование модулей; это местная политика, которая делает. Однако это может иметь смысл не во всех случаях, даже если это возможно.

4 голосов
/ 30 апреля 2013

Рискуя биркой некромантии, я решил добавить еще один из рюкзака Тима Тоади:

for (0 .. $#list) {
    next if $_ % 2;
    my ($i, $j) = @list[$_, $_ + 1];
    say "i:$i, j:$j";
}

Неразрушающий, без дубликатов списков, без переменных состояния и достаточно кратко.

4 голосов
/ 20 февраля 2009

Возможно, вы захотите создать простую подпрограмму, чтобы она работала на вас.

Я предлагаю это:

{
  my $cl_ind = 0;
  sub arrayeach(@) {
    my @obj = @_;
    if(($cl_ind+2) > @obj)
    {
      $cl_ind = 0;
      return;
    }
    $cl_ind+=2;
    return ($obj[$cl_ind-2],$obj[$cl_ind-1]);
  }
}

Закрытие заставляет его работать чисто. Чтобы использовать arrayeach (каждый из которых работает как хэш, не требуя опасного приведения к массиву:

my @temp = (1,2,3,4,5,6,1,2,3,4,5,6);
while( ($a,$b) = arrayeach(@temp)) {
  print "A $a AND $b\n";
}

Это не разрушительно.

3 голосов
/ 21 июня 2011
my $i;
for ( qw(a b c d) ) {
    if (!defined($i)) { $i = $_; next; }
    print STDOUT "i = $i, j = $_\n";
    undef($i);
}

Выходы:

i = a, j = b
i = c, j = d

Это также работает для списков, а не только для массивов.

3 голосов
/ 18 октября 2009

Как насчет функционального решения общего назначения.

use Carp; # so mapn can croak about errors

sub mapn (&$@) {
    my ($sub, $n, @ret) = splice @_, 0, 2;
    croak '$_[1] must be >= 1' unless $n >= 1;
    while (@_) {
        local *_ = \$_[0];
        push @ret, $sub->(splice @_, 0, $n)
    }
    @ret
}

sub by ($@) {mapn {[@_]} shift, @_}
sub every ($@); *every = \&by;

Функция mapn работает так же, как map, за исключением того, что первый аргумент после ее блока - это количество элементов, которое нужно взять. Он помещает первый элемент в $_ и все элементы в @_ .

print mapn {"@_\n"} 2 => 1 .. 5;
# prints
1 2
3 4
5

Следующие два идентичных подпрограммы by и every создают полезные наречия для различных циклических конструкций. Они обрабатывают список с помощью mapn и возвращают список ссылок на массив нужного размера

print "@$_\n" for every 2 => 1..10;

print map {"@$_\n"} grep {$_->[1] > 5} by 2 => 1..10;

Я считаю, что это более чистое и интуитивно понятное решение, чем natatime, или другое решение, например, стиль c для цикла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...