假设我有一个大哈希,我想迭代它的内容。标准的习语是这样的:
while(($key, $value) = each(%{$hash_ref})){
///do something
}
但是,如果我理解我的perl,这实际上是做了两件事。首先
%{$hash_ref}
正在将ref翻译成列表上下文。因此返回类似
的内容(key1, value1, key2, value2, key3, value3 etc)
将存储在我的堆栈内存中。然后每个方法都会运行,吃掉内存中的前两个值(key1& value1)并将它们返回到我的while循环来处理。
如果我对此的理解是正确的,这意味着我已经有效地将我的整个哈希复制到我的堆栈内存中,只是迭代新的副本,这对于大型哈希来说可能是昂贵的,因为迭代数组的代价很高两次,但也是由于潜在的缓存命中,如果两个哈希值都不能一次保存在内存中。看起来效率很低。我想知道这是不是真的发生了,或者我是否误解了实际行为,或者编译器是否为我的效率低下了?
跟进问题,假设我对标准行为是正确的。
是否有一种语法可以避免通过在原始哈希中迭代它来复制哈希值?如果没有哈希,那么有一个更简单的数组吗?
这是否意味着在上面的示例中,如果我修改了循环中的hash_ref内容,我可能会在哈希副本和实际哈希值之间得到不一致的值;导致$ value的值不同于$ hash_ref->($ key)?
答案 0 :(得分:5)
不,您引用的语法不会创建副本。
这个表达式:
%{$hash_ref}
完全等同于:
%$hash_ref
并假设$hash_ref
标量变量确实包含对哈希的引用,那么在前面添加%
就是“解除引用”。引用 - 即它解析为表示底层哈希值的值($hash_ref
指向的东西)。
如果查看each函数的文档,您会发现它希望将哈希作为参数。将%
放在前面是当你拥有hashref时提供哈希的方法。
如果您编写了自己的子例程并将哈希值传递给它:
my_sub(%$hash_ref);
然后在某种程度上你可以说哈希被复制了,因为在子例程中,特殊的@_
数组将包含来自哈希的所有键/值对的列表。但即使在这种情况下,@ _的元素实际上是键和值的别名。如果您执行了以下操作,您实际上只能获得副本:my @args = @_
。
Perl的内置each
函数声明了原型' +'它有效地将哈希(或数组)参数强制转换为对底层数据结构的引用。
另外,从版本5.14开始,each
函数也可以引用哈希。所以而不是:
($key, $value) = each(%{$hash_ref})
你可以简单地说:
($key, $value) = each($hash_ref)
答案 1 :(得分:1)
each
没有创建副本(尽管您通过分配将返回的值复制到$key
和$value
)。哈希本身传递给each
。
each
有点特别。它支持以下语法:
each HASH
each ARRAY
如您所见,它不接受任意表达。 (那将是each EXPR
或each LIST
)。这样做的原因是允许each(%foo)
将散列%foo
本身传递给each
,而不是在列表上下文中对其进行评估。 each
可以做到这一点,因为它是一个运算符,运算符可以有自己的解析规则。但是,您可以使用\%
原型执行类似操作。
use Data::Dumper;
sub f { print(Dumper(@_)); }
sub g(\%) { print(Dumper(@_)); } # Similar to each
my %h = (a=>1, b=>2);
f(%h); # Evaluates %h in list context.
print("\n");
g(%h); # Passes a reference to %h.
输出:
$VAR1 = 'a'; # 4 args, the keys and values of the hash
$VAR2 = 1;
$VAR3 = 'b';
$VAR4 = 2;
$VAR1 = { # 1 arg, a reference to the hash
'a' => 1,
'b' => 2
};
%{$h_ref}
与%h
相同,因此上述所有内容也适用于%{$h_ref}
。
请注意,即使哈希被展平,也不会复制哈希。键被复制",但这些值是直接返回的。
use Data::Dumper;
my %h = (abc=>"def", ghi=>"jkl");
print(Dumper(\%h));
$_ = uc($_) for %h;
print(Dumper(\%h));
输出:
$VAR1 = {
'abc' => 'def',
'ghi' => 'jkl'
};
$VAR1 = {
'abc' => 'DEF',
'ghi' => 'JKL'
};
您可以阅读有关此here的更多信息。