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

dso*_*len 6 perl hash

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

while(($key, $value) = each(%{$hash_ref})){
   ///do something
}
Run Code Online (Sandbox Code Playgroud)

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

%{$hash_ref}
Run Code Online (Sandbox Code Playgroud)

正在将ref翻译成列表上下文.因此返回类似的东西

(key1, value1, key2, value2, key3, value3 etc)
Run Code Online (Sandbox Code Playgroud)

它将存储在我的堆栈内存中.然后每个方法都会运行,在内存中使用前两个值(key1和value1)并将它们返回到我的while循环进行处理.

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

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

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

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

Gra*_*ean 5

不,您引用的语法不会创建副本.

这个表达式:

%{$hash_ref}
Run Code Online (Sandbox Code Playgroud)

完全等同于:

%$hash_ref
Run Code Online (Sandbox Code Playgroud)

并且假设$hash_ref标量变量确实包含对散列的引用,那么%在前面添加简单地"解除引用"引用 - 即它解析为表示底层散列($hash_ref指向的东西)的值.

如果查看每个函数的文档,您会看到它希望将哈希作为参数.把%你的东西放在hashref上是如何提供哈希的.

如果您编写了自己的子例程并将哈希传递给它,就像这样:

my_sub(%$hash_ref);
Run Code Online (Sandbox Code Playgroud)

然后在某种程度上你可以说哈希被"复制"了,因为在子程序中,特殊@_数组将包含来自哈希的所有键/值对的列表.但即使在这种情况下,@ _的元素实际上是键和值的别名.如果您执行以下操作,您实际上只能获得一份副本:my @args = @_.

Perl的内置each函数使用原型'+'声明,它有效地将哈希(或数组)参数强制转换为对底层数据结构的引用.

另外,从版本5.14开始,该each函数也可以引用哈希.所以代替:

($key, $value) = each(%{$hash_ref})
Run Code Online (Sandbox Code Playgroud)

你可以简单地说:

($key, $value) = each($hash_ref)
Run Code Online (Sandbox Code Playgroud)

  • @Hambone`每个$ ref`在5.14中作为*实验*特征引入,但稍后修订或删除.从5.24开始它*将被删除,因为它变得非常麻烦.取而代之的是"postderef"功能,在5.20中作为实验引入,并且在5.24中将升级为非实验性(在语法或行为上没有变化). (5认同)

ike*_*ami 1

不会创建任何副本each(尽管您确实将返回值复制到赋值中$key$value通过赋值)。哈希本身被传递到each.

each有点特别。它支持以下语法:

each HASH
each ARRAY
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它不接受任意表达式。(那就是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.
Run Code Online (Sandbox Code Playgroud)

输出:

$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
        };
Run Code Online (Sandbox Code Playgroud)

%{$h_ref}与 相同%h,因此以上所有内容%{$h_ref}也适用。


请注意,即使散列被展平,也不会被复制。键被“复制”,但值被直接返回。

use Data::Dumper;
my %h = (abc=>"def", ghi=>"jkl");
print(Dumper(\%h));
$_ = uc($_) for %h;
print(Dumper(\%h));
Run Code Online (Sandbox Code Playgroud)

输出:

$VAR1 = {
          'abc' => 'def',
          'ghi' => 'jkl'
        };
$VAR1 = {
          'abc' => 'DEF',
          'ghi' => 'JKL'
        };
Run Code Online (Sandbox Code Playgroud)

您可以在此处阅读有关此内容的更多信息。