Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
关于在Perl中正确使用解引用的困惑_Perl_Reference_Performance - Fatal编程技术网

关于在Perl中正确使用解引用的困惑

关于在Perl中正确使用解引用的困惑,perl,reference,performance,Perl,Reference,Performance,前几天,我注意到,在更改散列中的值时,在Perl中取消对散列的引用时,实际上是在复制该散列。为了证实这一点,我写了这个简短的脚本: #! 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

前几天,我注意到,在更改散列中的值时,在Perl中取消对散列的引用时,实际上是在复制该散列。为了证实这一点,我写了这个简短的脚本:

#! 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)
这让我意识到,在我的一些脚本中,可能有一些地方的行为不符合预期。为什么一开始就是这样?如果要传递或返回散列,那么更自然的假设是,取消对散列的引用将允许我更改被取消引用的散列的值。相反,我只是到处复制,没有任何真正的需要/理由让语法更加明显

我意识到,直到现在我还没有注意到这一点,这表明这可能没什么大不了的(就我所有的脚本都需要修复而言——但向前看很重要)。我认为这将是非常罕见的看到明显的性能差异,但这并不能改变我仍然困惑的事实


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

问题是您正在制作一份哈希副本,以便在此行中使用:

my %h2 = %{$hRef};
这是可以理解的,因为这里的许多帖子都使用这个习语来为散列生成本地名称,而没有解释它实际上是在生成副本

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

您要做的是直接使用引用

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

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

$$href{new_key} = 'new_value';
当使用普通散列时,您有一个sigil,当谈到整个散列时是
%
,当谈到单个元素时是
$
,当谈到一个片段时是
@
。然后,每个信号后面都有一个标识符

 %hash          # whole hash
 $hash{key}     # element
 @hash{qw(a b)} # slice
%{hash}
${hash}{key}
@{hash}{qw(a b)} 
要使用名为
$href
的引用,只需将上述代码中的字符串
哈希
替换为
$href
。换句话说,
$href
是标识符的完整名称:

%$href          # whole hash
$$href{key}     # element
@$href{qw(a b)} # slice
其中每一项都可以用更详细的形式编写,如下所示:

%{$href}
${$href}{key}
@{$href}{qw(a b)}
它再次将字符串
'$href'
替换为
'hash'
,作为标识符的名称

 %hash          # whole hash
 $hash{key}     # element
 @hash{qw(a b)} # slice
%{hash}
${hash}{key}
@{hash}{qw(a b)} 
使用图元时,也可以使用解引用箭头:

$hash->{key}  # exactly the same as $$hash{key}
但我更喜欢双重sigil语法,因为它类似于整个聚合和切片语法,以及正常的非引用语法

总之,每当你写这样的东西:

my @array = @$array_ref;
my %hash  = %$hash_ref;
您将制作每个聚合的第一级副本。直接使用解引用语法时,您将处理实际值,而不是副本


如果您想要哈希的真实本地名称,但希望使用相同的哈希,则可以使用
local
关键字创建别名

 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

您混淆了取消引用的操作,取消引用本身不会创建副本,而在列表上下文中使用哈希并分配该列表,这会造成混淆
$hashref->{'a'}
是一个解引用,但肯定会影响原始哈希。这对于
$#$arrayref
值(%%hashref)
也是如此

没有赋值,只有列表上下文
%$hashref
是一个混合beast;结果列表包含哈希键的副本,但实际哈希值的别名。您可以看到这一点:

$ perl -wle'$x={"a".."f"}; for (%$x) { $_=chr(ord($_)+10) }; print %$x'
epcnal
vs


但是,
%$hashref
的作用与这里的
%hash
没有任何不同。

如果perl按照您的建议执行,那么变量将很容易出现别名,这将更加混乱。实际上,您可以使用globbing来别名变量,但您需要显式地这样做

否,取消引用不会创建引用对象的副本。创建新变量的是
my

$ perl -E'
   my %h1; my $h1 = \%h1;
   my %h2; my $h2 = \%h2;
   say $h1;
   say $h2;
   say $h1 == $h2 ?1:0;
'
HASH(0x83b62e0)
HASH(0x83b6340)
0

$ perl -E'
   my %h;
   my $h1 = \%h;
   my $h2 = \%h;
   say $h1;
   say $h2;
   say $h1 == $h2 ?1:0;
'
HASH(0x9eae2d8)
HASH(0x9eae2d8)
1

不,
$#{$someArrayHashRef}
不会创建新数组。

非常好的解释-谢谢。我确实有一个关于注释
#local unwinds here的问题,该注释将全局值恢复到其以前的值(如果有)
。所以你在这里说的是,如果有另一个
我们的%hash在其他地方声明,在进入
some\u sub
时,它的值被更改,并且在退出
some\u sub
时被还原?如果是这种情况,是否可能出现线程问题,例如,
our%hash
some\u sub
中创建,但同时在
some\u other\u sub
中引用,这会产生意外的副作用?有两种情况。第一个是
我们的%hash
行声明标识符
%hash
将是子例程中
%package::hash
的别名(这只是防止每次写入包名的语法糖)。第二个是
local
关键字创建了一个新的动态范围,其中全局
%package::hash
变量有一个新值。一旦子例程返回(以任何方式),该范围将立即解除,但是,从该范围内调用的任何子例程都可以访问该变量(因为它毕竟是一个全局变量)。继续…由于
%package::hash
变量是全局变量,因此您需要确保没有其他子例程使用它<代码>本地
在这方面提供了一些保护,因为它为子例程创建了一个新变量。因此,在子例程调用之前,
%package::hash
可能是未定义的,并且将再次被定义