2015-04-12 133 views
10

是否有可能从Perl foreach循环中的数组中分配两个变量相同的数据?是否可以在Perl foreach循环中分配两个变量?

我使用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的'each'不是第二次遍历整个哈希?](http://stackoverflow.com/questions/1123693/why-doesnt-perls-each-iterate-through-the-entire- hash-the-second-time) –

回答

11

这不是在Perl 5的核心语言,但List::Utilpairs功能它应该足够接近(以及其他一些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

List :: Util是Perl 5.7.3以来的核心模块。所以可以假定为安装。 – BarneySchmale

+2

@BarneySchmale - 是的,我不太清楚如何清楚地说出来。它是核心_distribution_(你不必从CPAN安装)的一部分,但不是核心_language_的一部分(你必须明确地使用模块)。我接受建议(或编辑)以更好的方式说出答案。 –

+0

@BarneySchmale你可以依赖List :: Util,但不能在List :: Pairwise API中使用,它只在最近才添加。 – 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 perldscperldoc 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:我被要求澄清为什么数据结构是错误的。它是一组扁平化的元组(又名记录又名数据集)。通过计数每个数据的数量来重新创建元组。但是构建这个集合的读者有什么错误,并不总能得到正确的数字?如果缺少一个值,它只是跳过添加任何东西?然后所有剩余的元组都被移位一个,导致以下元组被错误地分组,因此无效。这就是为什么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

同意。我并不是说你的代码是坏的或者错误的,只是OP最初询问了一个“foreach”循环。你的解决方案和'foreach'循环之间的一个区别就是你的代码从数组中删除了项目。没有错,只是不同 - 因此“值得注意”。 –

9

最简单的方法来使用,这是一个while循环调用每个时间数组的前两个元素splice

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

然而,不同于foreach,这种持续和原始数组上两班,所以当你完成,该数组为空。此外,分配的变量是副本,而不是别名,如foreach

如果你不喜欢,你可以使用C风格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这是很容易使用。它也很容易实现 - 它实质上是对这里的回复中提到的解决方案的包装。

以下是不是最好的例子,但我已经使用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在自动包装方法->箭头传递给函数)仍然存在。所以,我可以这样做:

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] ...。从2009年Perl6降临Looping for Fun and Profit


你提到Perl6,这很好地处理多个循环值:

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方法见日历或Blocks and Statements Synopsis以了解详情。也许Perl 5将有一个类似的“循环由多个值”构造一天 - àlaperl5i's foreach :-)