是否可以在Perl foreach循环中为数组中的两个变量分配相同的数据?
我正在使用Perl 5,我想我在Perl 6中遇到了一些东西。
这样的事情:
my $var1;
my $var2;
foreach $var1,$var2 (@array){...}
答案 0 :(得分:13)
它不是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
答案 1 :(得分: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
答案 2 :(得分: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:我被要求澄清数据结构错误的原因。它是一组扁平的元组(也就是数据集的记录)。通过计算每个元组的数据来重新创建元组。但构建该集合的读者有什么错误,并不总能得到正确的数字?如果,对于缺失值,它只是跳过添加任何东西?然后所有剩余的元组都被移动一个,导致后面的元组被错误地分组,因此无效。这就是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
}
答案 3 :(得分: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 shell):
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
箭头前面的->
传递给功能)仍在那里。所以我可以这样做:
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,它可以很好地处理多个循环值:
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方法的更多信息,请参阅2009 Perl6 Advent Calendar中的Looping for Fun and Profit或Blocks and Statements Synopsis以获取详细信息。或许Perl 5将有一天类似的“多个循环值循环”构造 - àla perl5i's foreach
:-)