在不更改输入的情况下将哈希传递给子例程

时间:2018-12-22 21:33:36

标签: perl

我正在尝试在Perl中使用哈希处理时调试一些奇怪的行为。 我正在将哈希(而不是ref)传递给子例程,并且由于某种原因它会对其进行更新。

some_sub($a,%{$hash});
sub some_sub {
    my ($a,%hash) = @_;
    my @struct;
    while (my ($dir, $data) = each %hash) {
        foreach my $id (keys(%{$data})) {
            my $entry = $data->{$id};
            $entry->{id} = $id;
            my $parent = $data->{$entry->{id}};
            unless ($parent) {
                push @struct, $entry
            } else {
                push @{$parent->{children}},$entry;
            }
        }
    }
}
my %h= %{$hash};
print Dumper(\%h);

some_sub确实更改了%hash,但仅更改了内部范围,因此不应更改外部%hash的数据。另外,我将散列作为散列而不是散列引用传递。我怀疑子some_sub会将存储器地址插入%hash中,但不确定。

我应该如何调试和解决此问题?

编辑:我还尝试了将哈希引用传递给子例程,并在对新哈希执行所有操作时将哈希引用解引用到另一个哈希中。

2 个答案:

答案 0 :(得分:9)

哈希中的每个值都是标量。如果您有嵌套的哈希,则内部哈希存储为标量-哈希引用。因此,在更改嵌套结构时,更改也会发生在引用的哈希中,该哈希也从原始哈希中引用。

#! /usr/bin/perl
use warnings;
use strict;

sub change {
    my %hash2 = @_;
    for my $key (keys %hash2) {
        ++$_ for values $hash2{$key};
    }
}

my %hash = (a => {b => 12, c => 24});
change(%hash);
use Data::Dumper; print Dumper \%hash;

输出:

$VAR1 = {
          'a' => {
                   'b' => 13,
                   'c' => 25
                 }
        };

获得与原始结构相似但包含不同引用的结构的过程称为克隆深度复制。请参见Clone中的Storabledclone

答案 1 :(得分:4)

参数作为标量的平面列表传递给函数,因此

some_sub($a, %{$hash})

$a

之后将哈希的键和值作为列表传递
some_sun($a, key, value, ...);

将这些键值对分配给函数中的哈希值之后,您便可以直接使用引用(您的哈希值),因此调用者中的数据将被更改。

没有说明目的是什么,但是如果您不希望调用者的数据更改一个解决方法,则是避免更改子程序中的数据,也许是通过为处理遇到的每个引用引入局部变量。如果它们本身可能包含引用,您仍然需要非常小心。

创建散列的完整深层副本较为简单,如果数据结构并不庞大。例如

use Storable qw(dclone);

some_sub($v, $hashref);

sub some_sub {
    my ($var, $hr) = @_;
    my $cloned_hashref = dclone($hr);
    # work away with $cloned_hashref
}