关于在 Perl 中正确使用取消引用的困惑 [英] Confusion about proper usage of dereference in Perl

查看:54
本文介绍了关于在 Perl 中正确使用取消引用的困惑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前几天我注意到 - 在更改散列中的值时 - 当您在 Perl 中取消引用一个散列时,您实际上是在制作该散列的副本.为了确认我写了这个快速的小脚本:

I noticed the other day that - while altering values in a hash - that when you dereference a hash in Perl, you actually are making a copy of that hash. To confirm I wrote this quick little script:

#! perl
use warnings;
use strict;

my %h = ();
my $hRef = \%h;
my %h2 = %{$hRef};
my $h2Ref = \%h2;

if($hRef eq $h2Ref) {
  print "\n\tThey're the same $hRef $h2Ref";
}
else {
  print "\n\tThey're NOT the same $hRef $h2Ref";
}
print "\n\n";

输出:

    They're NOT the same HASH(0x10ff6848) HASH(0x10fede18)

这让我意识到我的一些脚本中可能存在一些行为不符合预期的地方.为什么它首先是这样的?如果您正在传递或返回一个散列,则更自然地假设取消引用散列将允许我更改被取消引用的散列的值.相反,我只是在没有任何真正需要/理由的情况下到处复制副本,从而使语法更加明显.

This leads me to realize that there could be spots in some of my scripts where they aren't behaving as expected. Why is it even like this in the first place? If you're passing or returning a hash, it would be more natural to assume that dereferencing the hash would allow me to alter the values of the hash being dereferenced. Instead I'm just making copies all over the place without any real need/reason to beyond making syntax a little more obvious.

我意识到直到现在我都没有注意到这一点,这表明这可能不是什么大问题(就需要在我的所有脚本中进行修复 - 但重要的是向前发展).我认为很少会看到明显的性能差异,但这并不能改变我仍然感到困惑的事实.

I realize the fact that I hadn't even noticed this until now shows its probably not that big of a deal (in terms of the need to go fix in all of my scripts - but important going forward). I think its going to be pretty rare to see noticeable performance differences out of this, but that doesn't alter the fact that I'm still confused.

这是 perl 的设计吗?有什么我不知道的明确原因吗?或者这只是已知并且您 - 作为程序员 - 希望知道并相应地编写脚本?

Is this by design in perl? Is there some explicit reason I don't know about for this; or is this just known and you - as the programmer - expected to know and write scripts accordingly?

推荐答案

问题是您正在制作哈希的副本以在此行中使用:

The problem is that you are making a copy of the hash to work with in this line:

my %h2 = %{$hRef};

这是可以理解的,因为 SO 上的许多帖子都使用该习语为哈希创建本地名称,而没有解释它实际上是在制作副本.

And that is understandable, since many posts here on SO use that idiom to make a local name for a hash, without explaining that it is actually making a copy.

在 Perl 中,散列是复数值,就像数组一样.这意味着在列表上下文中(例如您在分配给散列时得到的)聚合被分解成其内容列表.这个对的列表然后被组装成一个新的散列,如图所示.

In Perl, a hash is a plural value, just like an array. This means that in list context (such as you get when assigning to a hash) the aggregate is taken apart into a list of its contents. This list of pairs is then assembled into a new hash as shown.

您想要做的是直接使用参考.

What you want to do is work with the reference directly.

for (keys %$hRef) {...}
for (values %$href) {...}

my $x = $href->{some_key};
# or
my $x = $$href{some_key};

$$href{new_key} = 'new_value';

当使用普通散列时,你有一个符号,在谈论整个散列时是 %,在谈论单个元素时是 $,以及@ 在谈论切片时.这些符号中的每一个都后跟一个标识符.

When working with a normal hash, you have the sigil which is either a % when talking about the entire hash, a $ when talking about a single element, and @ when talking about a slice. Each of these sigils is then followed by an identifier.

 %hash          # whole hash
 $hash{key}     # element
 @hash{qw(a b)} # slice

要使用名为 $href 的引用,只需将上述代码中的字符串 hash 替换为 $href.换句话说,$href 是标识符的完整名称:

To work with a reference named $href simply replace the string hash in the above code with $href. In other words, $href is the complete name of the identifier:

%$href          # whole hash
$$href{key}     # element
@$href{qw(a b)} # slice

这些都可以写成更详细的形式:

Each of these could be written in a more verbose form as:

%{$href}
${$href}{key}
@{$href}{qw(a b)}

再次将字符串 '$href' 替换为 'hash' 作为标识符的名称.

Which is again a substitution of the string '$href' for 'hash' as the name of the identifier.

%{hash}
${hash}{key}
@{hash}{qw(a b)} 

您还可以在处理元素时使用取消引用箭头:

You can also use a dereferencing arrow when working with an element:

$hash->{key}  # exactly the same as $$hash{key}

但我更喜欢双符号语法,因为它类似于整个聚合和切片语法,以及普通的非引用语法.

But I prefer the doubled sigil syntax since it is similar to the whole aggregate and slice syntax, as well as the normal non-reference syntax.

总而言之,任何时候你写这样的东西:

So to sum up, any time you write something like this:

my @array = @$array_ref;
my %hash  = %$hash_ref;

您将复制每个聚合的第一级.直接使用解引用语法时,您将处理实际值,而不是副本.

You will be making a copy of the first level of each aggregate. When using the dereferencing syntax directly, you will be working on the actual values, and not a copy.

如果您想要一个真实的本地名称作为一个散列,但想要处理相同的散列,您可以使用 local 关键字来创建一个别名.

If you want a REAL local name for a hash, but want to work on the same hash, you can use the local keyword to create an alias.

 sub some_sub {
    my $hash_ref = shift;
    our %hash; # declare a lexical name for the global %{__PACKAGE__::hash}
    local *hash = \%$hash_ref;
        # install the hash ref into the glob
        # the `\%` bit ensures we have a hash ref

    # use %hash here, all changes will be made to $hash_ref

 }  # local unwinds here, restoring the global to its previous value if any

那是纯粹的 Perl 别名方式.如果要使用 my 变量来保存别名,可以使用模块 Data::Alias

That is the pure Perl way of aliasing. If you want to use a my variable to hold the alias, you can use the module Data::Alias

这篇关于关于在 Perl 中正确使用取消引用的困惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆