Как читать два элемента за раз в цикле 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 ]

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

Как объясняет Мирод, кода не так много. Вот почти все, что вам нужно. (Обратите внимание, что у меня нет никаких проверок для нечетных списков или тому подобное.)

#!/usr/bin/env perl
use strict;
use warnings;

my @list = qw/1 2 3 4 5 6/;
my $get_em = get_by(2, @list);

while ( my ($i, $j) = $get_em->() ) {
  print "i: $i, j: $j\n";
}

sub get_by {
  my $n = shift;
  my @list = @_;

  return sub {
    return splice @list, 0, $n;
  }
}
1 голос
/ 15 декабря 2015

быстрое решение для небольших массивов:

for ( map {$_*2} 0..@list/2-1 ){
    my ($i, $j) = @list[$_,$_+1];
    print "i: $i, j:$j\n";
}

какой-то лайнер

данные:

@v = (a=>1, b=>2, c=>3);

это

print join ', ', map{sprintf '%s:%s', $v[$_], $v[$_+1]} grep {!($_%2)} 0..$#v

или что-то в этом роде

print join ', ', map {sprintf '%s:%s', @v[$_,$_+1]} map {$_*2} 0..@v/2-1;

результат тот же

a:1, b:2, c:3
1 голос
/ 20 февраля 2009

Использование цикла for сделает то, что вам нужно.

use strict;
use warnings;

my @list = qw(1 2 3 4 5 );
my $i = 0;

for ($i = 0; $i < scalar(@list); $i++)
{
    my $a = $list[$i];
    my $b = $list[++$i];
    if(defined($a)) {
        print "a:$a";
    }
    if(defined($b)) {
        print "b:$b";
    }   
    print "\n";
}

edit : я исправил свой пост, чтобы использовать скалярную функцию для получения размера массива, а также добавить некоторую проверку в случае, если массив не содержит четного числа элементов.

0 голосов
/ 09 января 2018

другой подход, не полностью чистый, но пригодный для использования. каждый создает итератор, вы можете использовать его дважды. когда параметр является классическим массивом, он возвращает индекс и значение, пожалуйста, прочитайте это: https://perldoc.perl.org/functions/each.html

Итак, ваш код может быть таким:

my @array=qw(one two three four five); #five element as unpaired will be ignored
while (my ($i1,$one,$i2,$two)=(each(@array),each(@array)) {
  #we will use $ix for detect end of array
  next unless defined $i1 and defined $i2; #secure complete end of array
  print "fetched array elements: $one => $two\n";
};

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

0 голосов
/ 23 апреля 2015

Это может быть сделано неразрушающим, с просто фантастическим Эрика Строма List::Gen:

perl -MList::Gen=":utility" -E '@nums = "1" .. "6" ; 
      say "i:$_->[0] j:$_->[1]" for every 2 => @nums'

выход

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

Редактировать (добавить версию без CPAN):

Массив и стиль C для цикла à la Брайан Д Фой и Том Кристиансен ! Это можно прочитать как «использовать индекс ($i) для циклического перебора элементов @list foreach $n одновременно»:

use v5.16; # for strict, warnings, say

my @list = "1" .. "6";
my $n = 2 ;   # the number to loop by
$n-- ;        # subtract 1 because of zero index

foreach (my $i = 0 ; $i < @list ; $i += $n ) { 
  say "i:", [ @list[$i..$i+$n] ]->[0], " j:", [ @list[$i..$i+$n] ]->[1];
  $i++ ;          
}

Мы получаем доступ к элементам (->[0]) анонимного массива ([ ]). Для более обобщенного вывода срез интерполированного массива может использоваться сам по себе, , например, : print "@list[$i..$i+$n]";, изменяя значение $n, как требуется.

0 голосов
/ 17 апреля 2015

Я думаю, что более простой способ состоит в том, чтобы использовать старого бедного «друг» Прямо так:

while (my ($key,$value) = each @list) {
        print "$key=$value\n";
}

Обновлен:

Да, это неправильно. Сначала нужно преобразовать список в хеш, но он может быть слишком сложным:

my %hash = (@list);
while (my ($key,$value) = each %hash) {
        print "$key=$value\n";
}
0 голосов
/ 21 февраля 2009

вот реализация natatime, которая не создает копию списка:

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

  sub {
    return splice @$list, 0, $n;
  }
}

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

Я придумал этот код для решения аналогичного требования:

sub map_pairs(&\@) {
    my $op = shift;
    use vars '@array';
    local *array = shift;    # make alias of calling array

    return () unless @array;

    # Get package global $a, $b for the calling scope
    my ($caller_a, $caller_b) = do {
        my $pkg = caller();
        no strict 'refs';
        \*{$pkg.'::a'}, \*{$pkg.'::b'};
    };

    # Get index counter size.
    my $limit = $#array/2;

    # Localize caller's $a and $b
    local(*$caller_a, *$caller_b);

    # This map is also the return value
    map {
        # assign to $a, $b as refs to caller's array elements
        (*$caller_a, *$caller_b) = \($array[$_], $array[$_+1]);
        $op->();    # perform the transformation
    } 
    map { 2 * $_ } 0..$limit;  # get indexes to operate upon.
}

Вы используете это так:

@foo = qw( a 1 b 2 c 3 );
my @bar = map_pairs { "$a is $b" } @foo;

чтобы получить:

@bar = ( 'a is 1', 'b is 2', 'c is 3' );

Я намеревался отправить сопровождающему List :: MoreUtils, но у меня нет версии для XS.

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