2015-04-12 5 views
10

Возможно ли присвоить две переменные одни и те же данные из массива в Perl foreach?Можно ли назначить две переменные в Perl foreach loop?

Я использую Perl 5, я думаю, что я наткнулся на что-то в Perl 6.

Что-то вроде этого:

my $var1; 
my $var2; 

foreach $var1,$var2 (@array){...} 
+1

Вы ищете ['each'] (http://perldoc.perl.org/functions/ each.html)? Он иллюстрирует 'while (($ key, $ value) = each% ENV) { print" $ key = $ value \ n "; } 'среди прочих слов мудрости. Остерегайтесь поведения перезагрузки. См. Также [Почему во всех случаях хэширование Perl «каждый» повторяется во второй раз?] (Http://stackoverflow.com/questions/1123693/why-doesnt-perls-each-iterate-through-the-entire- hash-the-second-time) –

ответ

11

Это не в базовом языке Perl 5, но List::Util имеет pairs функцию который должен быть достаточно близко (и ряд других pair... функций, которые могут быть более удобным, в зависимости от того, что вы делаете внутри цикла):

#!/usr/bin/env perl  

use strict; 
use warnings; 
use 5.010; 

use List::Util 'pairs'; 

my @list = qw(a 1 b 2 c 3); 

for my $pair (pairs @list) { 
    my ($first, $second) = @$pair; 
    say "$first => $second"; 
} 

Выход:

a => 1 
b => 2 
c => 3 
+2

Список :: Util - это основной модуль с Perl 5.7.3. Таким образом, можно считать, что это инстант. – BarneySchmale

+2

@BarneySchmale - Да, я не был уверен, как это ясно сказать. Это часть основного _distribution_ (вам не нужно устанавливать из CPAN), но не является частью основного _language_ (вам нужно явно «использовать» модуль). Я открыт для предложений (или изменений), чтобы лучше сказать, что в ответе. –

+0

@BarneySchmale Вы можете рассчитывать на List :: Util, но не на API List :: Pairwise, который был добавлен совсем недавно. – tchrist

2

Общий алгоритм более 2-х переменных:

while(@array){ 
    my $var1 = shift @array; 
    my $var2 = shift @array; 
    my $var3 = shift @array; 
    # other variables from @array 

    # do things with $var1, $var2, $var3, ... 
} 

PS: Используя рабочую копию массива, что она сохраняется для последующего использования:

if(my @working_copy = @array){ 
    while(@working_copy){ 
    my $var1 = shift @working_copy; 
    my $var2 = shift @working_copy; 
    my $var3 = shift @working_copy; 
    # other variables from @working_copy 

    # do things with $var1, $var2, $var3, ... 
    } 
} 

PPS: другой способ - использовать индексирование. Конечно, это верный признак неправильной структуры данных. Он должен быть массивом массивов (AoA) или массивом хэшей (AoH). См. perldoc perldsc и perldoc perllol.

my $i = 0; 
while($i < @array){ 
    my $var1 = $array[ $i++ ]; 
    my $var2 = $array[ $i++ ]; 
    my $var3 = $array[ $i++ ]; 
    # other variables from @array 

    # do things with $var1, $var2, $var3, ... 
} 

PPPS: Меня попросили уточнить, почему структура данных неверна. Это сплюснутый набор кортежей (он же записывает aka datasets). Кортежи воссоздаются путем подсчета количества данных для каждого. Но что у читателя, строящего набор, есть ошибка, и он не всегда имеет правильное число? Если для отсутствующего значения он просто пропускает добавление чего-либо? Затем все остальные кортежи сдвигаются на единицу, что приводит к неправильному группированию следующих кортежей и, следовательно, недействительным. Вот почему AoA лучше; только кортеж с отсутствующими данными будет недействительным.

Но лучшей структурой будет AoH. К каждой точке доступа будет доступ ключ. Затем можно добавить новые или дополнительные данные без нарушения кода ниже по течению.

В то время как я на него, я добавлю некоторые примеры кода:

# example code for AoA 
for my $tuple (@aoa){ 
    my $var1 = $tuple->[0]; 
    my $var2 = $tuple->[1]; 
    my $var3 = $tuple->[2]; 
    # etc 
} 

# example code for AoH 
for my $tuple (@aoh){ 
    my $var1 = $tuple->{keyname1}; 
    my $var2 = $tuple->{key_name_2}; 
    my $var3 = $tuple->{'key name with spaces'}; 
    my $var4 = $tuple->{$key_name_in_scalar_variable}; 
    # etc 
} 
+2

Стоит отметить, что после завершения этого цикла '@ array' будет пустым. Цикл 'foreach' не имеет этого побочного эффекта. –

+1

Но цикл 'foreach' не будет работать. – shawnhcorey

+1

Согласовано. Я не говорю, что ваш код плохой или неправильный, просто потому, что ОП изначально спрашивал о цикле 'foreach'. И одна точка различия между вашим решением и циклом foreach заключается в том, что ваш код удаляет элементы из массива. Не так, просто разные - значит «стоит отметить». –

9

Самый простой способ использовать это с while цикл, который вызывает splice на первых двух элементов массива каждый раз ,

while (my($var1, $var2) = splice(@array, 0, 2)) { 
    ... 
} 

Однако, в отличие от foreach, это постоянно делает двойной сдвиг на исходном массиве, так что, когда вы закончите, массив пуст. Кроме того, назначенными переменными являются копии, а не псевдонимы, такие как foreach.

Если вам не нравится, что вы можете использовать в стиле С for цикла:

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

Это оставляет массив на месте, но не позволяет обновлять его пути foreach делает. Для этого вам нужно напрямую обратиться к массиву.

my @pairlist = (
    fee => 1, 
    fie => 2, 
    foe => 3, 
    fum => 4, 
); 

for (my $i = 0; $i < @pairlist; $i += 2) { 
    $pairlist[ $i + 0 ] x= 2; 
    $pairlist[ $i + 1 ] *= 2; 
} 

print "Array is @pairlist\n"; 

Это печатает:

Array is feefee 2 fiefie 4 foefoe 6 fumfum 8 

Вы можете получить те в псевдонимами переменных, если вы попытаетесь достаточно трудно, но это, наверное, не стоит:

my @kvlist = ( 
    fee => 1, 
    fie => 2, 
    foe => 3, 
    fum => 4, 
); 

for (my $i = 0; $i < @kvlist; $i += 2) { 
    our ($key, $value); 
    local(*key, $value) = \@kvlist[ $i, $i + 1 ]; 
    $key x= 2; 
    $value *= 2; 
} 

print "Array is @kvlist\n"; 

Какие выводит ожидаемый измененный массив:

Array is feefee 2 fiefie 4 foefoe 6 fumfum 8 

Обратите внимание, что пары, предлагаемые List::Pairwise модуля, которые были, но очень недавно добавили к основным List::Util модулю (и поэтому вы, вероятно, не можете использовать его), до сих пор не дает вам псевдонимы:

use List::Util 1.29 qw(pairs); 

my @pairlist = (
    fee => 1, 
    fie => 2, 
    foe => 3, 
    fum => 4, 
); 

for my $pref (pairs(@pairlist)) { 
    $pref->[0] x= 2; 
    $pref->[1] *= 2; 
} 

print "Array is @pairlist\n"; 

это печатает только:

Array is fee 1 fie 2 foe 3 fum 4 

Так что не изменял массив вообще. К сожалению. :(

Конечно, если бы это было реальной хэш, вы можете удвоить значения тривиально:.

for my $value (values %hash) { $value *= 2 } 

причин того, что работает, потому что те являются псевдонимами в фактические значения хэш

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

my %old_hash = (
    fee => 1, 
    fie => 2, 
    foe => 3, 
    fum => 4, 
); 

my %new_hash;  
@new_hash{ map { $_ x 2 } keys %old_hash } = 
      map { $_ * 2 } values %old_hash; 

print "Old hash is: ", join(" " => %old_hash), "\n"; 
print "New hash is: ", join(" " => %new_hash), "\n"; 

Это выводит

Old hash is: foe 3 fee 1 fum 4 fie 2 
New hash is: foefoe 6 fiefie 4 fumfum 8 feefee 2 
0

Здесь модуль меньше способ «цикл» произвольное значение ($by) и выход в результате группы элементов, используя срез массива:

#!perl -l 
@array = "1".."6"; 
$by = 3; $by--; 

for (my $i = 0 ; $i < @array ; $i += $by) { 
     print "@array[$i..$i+$by]"; 
     $i++ ; 
} 

В однострочнике к тест (вырезать и вставить в Unix оболочки):

perl -E '@array = "1".."6"; $by = 3; $by--; 
     for (my $i = 0 ; $i < @array ; $i += $by) { 
     say "@array[$i..$i+$by]"; $i++ }' 

Выход:

1 2 3 
4 5 6 

Если вы сделаете $by = 2;, он напечатает пары чисел. Чтобы получить определенные элементы результирующего среза, получим его как анонимный массив: (, например[@array[$i..$i+$by]]->[1]).

Смотрите также:

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

Ниже не самый хороший пример, но я использую autobox::Core и сделал «метод» @array->natatime() ;-) так:

use autobox::Core ; 
sub autobox::Core::ARRAY::natatime { 
    my ($self, $by) = @_; 
    my @copy = @$self ; 
    my @array ; 

    push @array, [splice (@copy, 0, $by) ] while @copy ; 

    if (not defined wantarray) { 
     print "@{ $_ } \n" for @array ; 
    } 

    return wantarray ? @array : \@array;  
} 

@copy массив является spliced деструктивно, но $self (который как @array перед методом autobox -> стрелка переходит к функции) все еще существует. Так что я могу сделать:

my @dozen = "1" .. "12" ;   # cakes to eat 
@dozen->natatime(4) ;    # eat 4 at time 
my $arr_ref = @dozen->natatime(4) ; # make a reference 
say "Group 3: @{ $arr_ref->[2] }" ; # prints a group of elements 
say scalar @dozen , " cakes left" ; # eat cake; still have it 

Выход:

1 2 3 4 
5 6 7 8 
9 10 11 12 
Group 3: 9 10 11 12 
12 cakes left 

Еще один подход, который также использует модуль CPAN (я дал this answer elsewhere но это стоит повторить). Это также может быть сделано неразрушающим с Eric Strom's отличным List::Gen модуля:

perl -MList::Gen=":all" -E '@n = "1".."6"; say "@$_" for every 2 => @n' 
1 2 
3 4 
5 6 

Каждая группа элементов, которые вы захватите возвращается в анонимный массив поэтому отдельные значения находятся в: $_->[0] $_->[1] ...и т.д..


Вы упомянули Perl6, который обрабатывает несколько значений LOOPING красиво:

my @qarr = 1 .. 6; 
my ($x, $y, $z) ; 
for @qarr -> $x , $y , $z { say $x/$y ; say "z = " ~ $z } 

Выход:

0.5 
z = 3 
0.8 
z = 6 

Более подробную информацию о подходе Perl6 см: Looping for Fun and Profit от 2009 Perl6 Адвента Календарь, или Blocks and Statements Synopsis. Возможно, Perl 5 будет иметь аналогичную конструкцию «цикл по нескольким значениям» один день - à laperl5i's foreach :-)