是perl';每个函数都值得使用吗?
我们从中读到: 每个散列都有一个迭代器,由程序中的所有是perl';每个函数都值得使用吗?,perl,hash,iterator,each,Perl,Hash,Iterator,Each,我们从中读到: 每个散列都有一个迭代器,由程序中的所有每个、键和值函数调用共享;它可以通过读取散列中的所有元素,或通过计算键散列或值散列来重置 离开包含each()的作用域时,迭代器不会重置,这可能会导致错误: my %h = map { $_, 1 } qw(1 2 3); while (my $k = each %h) { print "1: $k\n"; last } while (my $k = each %h) { print "2: $k\n" } 输出: 1: 1 2
每个
、键
和值
函数调用共享;它可以通过读取散列中的所有元素,或通过计算键散列
或值散列
来重置
离开包含each()
的作用域时,迭代器不会重置,这可能会导致错误:
my %h = map { $_, 1 } qw(1 2 3);
while (my $k = each %h) { print "1: $k\n"; last }
while (my $k = each %h) { print "2: $k\n" }
输出:
1: 1
2: 3
2: 2
这种行为的常见解决方法是什么?一般来说,每种代码都值得使用吗?我认为只要你意识到这一点,就值得使用。当您在迭代中同时需要键和值时,它是理想的:
while (my ($k,$v) = each %h) {
say "$k = $v";
}
在您的示例中,可以通过添加键%h重置迭代器代码>像这样:
my %h = map { $_ => 1 } qw/1 2 3/;
while (my $k = each %h) { print "1: $k\n"; last }
keys %h; # reset %h
while (my $k = each %h) { print "2: $k\n" }
从Perl 5.12开始,还将允许对数组进行迭代。使用该函数重置迭代器。有关更多信息,请参见我发现每一个
对于这样的习惯用法都非常方便:
my $hashref = some_really_complicated_method_that_builds_a_large_and_deep_structure();
while (my ($key, $value) = each %$hashref)
{
# code that does stuff with both $key and $value
}
将该代码与以下代码进行对比:
my $hashref = ...same call as above
foreach my $key (keys %$hashref)
{
my $value = $hashref->{$key};
# more code here...
}
在第一种情况下,$key
和$value
可立即用于循环体。在第二种情况下,必须首先获取$value
。此外,$hashref
的键列表可能非常庞大,这会占用内存。这有时是一个问题<代码>每个
都不会产生此类开销
但是,
每一个
的缺点并不是很明显:如果提前中止循环,哈希的迭代器不会重置。另外(我发现这个问题更严重,甚至更不明显):你不能在这个循环中调用keys()
、values()
或另一个each()
。这样做将重置迭代器,您将失去在while循环中的位置。while循环将永远持续下去,这肯定是一个严重的错误。每一个都不仅值得使用,而且如果你想循环一个对内存来说太大的绑定哈希,它几乎是强制性的
在开始循环之前,一个空的上下文keys()(或值,但一致性很好)是唯一需要的“解决方法”;您寻找其他解决方法是有原因的吗?最好将其命名为:
每一个
。如果你的意思是“给我第一个键值对”,或者“给我前两个键值对”或者其他什么,那么使用它可能是错误的。请记住,这个想法非常灵活,每次调用它时,都会得到下一对(或标量上下文中的键) 每种
都太危险了,无法使用,许多样式指南完全禁止使用。危险在于,如果在散列结束之前中止每个的循环,则下一个循环将从那里开始。这会导致很难复制错误;程序的一部分的行为将取决于程序中完全不相关的另一部分。您可能会使用每一个对,但是您编写的每一个可能使用您的hash(或hashref;它是相同的)的模块呢
键
和值
总是安全的,所以只需使用它们即可<代码>键
使以确定性顺序遍历哈希变得更容易,这几乎总是更有用。(对于我的$key(排序键%hash){…}
)每个
都有一个内置的隐藏全局变量,它可能会伤害您。除非您需要这种行为,否则只使用键
更安全
考虑一下这个例子,我们希望对k/v对进行分组(是的,我知道printf
会做得更好):
在实际应用程序中调试上述测试中显示的错误将是非常痛苦的。(为了更好地输出,请使用Test::Differences
eq\u或_diff
而不是is
)
当然,one()
可以通过使用键来清除子例程开始和结束处的迭代器来修复。如果你还记得的话。如果你所有的同事都记得的话。只要没有人忘记,它是绝对安全的
我不知道你的情况,但我会坚持使用键
和值
如果你在一个绑定的散列中迭代,例如一个包含数百万个键的数据库,那么每个()都会更有效;这样,您就不必加载内存中的所有键。我想常见的解决方法包括计算键哈希值
或值哈希值
非常好!这是我能想到的使用每一个的最好(唯一?)理由。使用很多全局哈希,是吗?不管它是全局还是非全局。即使是类的私有属性也容易出现此问题。任何返回对散列的引用的操作都会受到影响。
#!perl
use strict;
use warnings;
use Test::More 'no_plan';
{ my %foo = map { ($_) x 2 } (1..15);
is( one( \%foo ), one( \%foo ), 'Calling one twice works with 15 keys' );
is( two( \%foo ), two( \%foo ), 'Calling two twice works with 15 keys' );
}
{ my %foo = map { ($_) x 2 } (1..105);
is( one( \%foo ), one( \%foo ), 'Calling one twice works with 105 keys' );
is( two( \%foo ), two( \%foo ), 'Calling two twice works with 105 keys' );
}
sub one {
my $foo = shift;
my $r = '';
for( 1..9 ) {
last unless my ($k, $v) = each %$foo;
$r .= " $_: $k -> $v\n";
}
for( 10..99 ) {
last unless my ($k, $v) = each %$foo;
$r .= " $_: $k -> $v\n";
}
return $r;
}
sub two {
my $foo = shift;
my $r = '';
my @k = keys %$foo;
for( 1..9 ) {
last unless @k;
my $k = shift @k;
$r .= " $_: $k -> $foo->{$k}\n";
}
for( 10..99 ) {
last unless @k;
my $k = shift @k;
$r .= " $_: $k -> $foo->{$k}\n";
}
return $r;
}