迭代哈希引用是否需要在perl中隐式复制它?

时间:2016-01-06 00:02:48

标签: perl hash

假设我有一个大哈希,我想迭代它的内容。标准的习语是这样的:

while(($key, $value) = each(%{$hash_ref})){
   ///do something
}

但是,如果我理解我的perl,这实际上是做了两件事。首先

%{$hash_ref}

正在将ref翻译成列表上下文。因此返回类似

的内容
(key1, value1, key2, value2, key3, value3 etc)

将存储在我的堆栈内存中。然后每个方法都会运行,吃掉内存中的前两个值(key1& value1)并将它们返回到我的while循环来处理。

如果我对此的理解是正确的,这意味着我已经有效地将我的整个哈希复制到我的堆栈内存中,只是迭代新的副本,这对于大型哈希来说可能是昂贵的,因为迭代数组的代价很高两次,但也是由于潜在的缓存命中,如果两个哈希值都不能一次保存在内存中。看起来效率很低。我想知道这是不是真的发生了,或者我是否误解了实际行为,或者编译器是否为我的效率低下了?

跟进问题,假设我对标准行为是正确的。

  1. 是否有一种语法可以避免通过在原始哈希中迭代它来复制哈希值?如果没有哈希,那么有一个更简单的数组吗?

  2. 这是否意味着在上面的示例中,如果我修改了循环中的hash_ref内容,我可能会在哈希副本和实际哈希值之间得到不一致的值;导致$ value的值不同于$ hash_ref->($ key)?

2 个答案:

答案 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 EXPReach 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的更多信息。