对阵列的修改也会更改其他阵列

时间:2015-10-21 15:40:16

标签: arrays perl subroutine

我在Perl中有两个全局多维数组@p@p0e。这是遗传算法的一部分,我希望将某些密钥从@p保存到@p0e。然后对@p进行修改。有几个子程序可以对@p进行修改,但是某些子程序偶尔(不是每次迭代)对@p进行修改也会导致@p0e被修改(虽然@p0e不应受到影响,但它会收到相同的密钥。

# this is the sub where part of @p is copied to @p0e
sub saveElite {
    @p0e = (); my $i = 0;

    foreach my $r (sort({$a<=>$b} keys $f{"rank"})) {
        if ($i<$elN) {
            $p0e[$i] = $p[$f{"rank"}{$r}]; # save chromosome
        }
        else {last;}
        $i++;
    }
}

# this is the sub that then sometimes changes @p0e
sub mutation {
    for (my $i=0; $i<@p; $i++) {
        for (my $j=0; $j<@{$p[$i]}; $j++) {
            if (rand(1)<=$mut) { # mutation
                $p[$i][$j] = mutate($p[$i][$j]);
            }
        }
    }
}

我想也许我以某种方式创建了对原始数组的引用而不是副本,但是因为这种意外的行为在每次迭代中都不会发生,所以情况并非如此。

3 个答案:

答案 0 :(得分:2)

我认为你的问题可能就是这样:

$p0e[$i] = $p[$f{"rank"}{$r}]; # save chromosome

因为看起来@p是一个多维数组。

问题是 - perl'做多维数组的方式是通过引用数组。因此,如果您复制内部数组,则可以通过引用进行复制。

E.g:

#!c:\Strawberry\perl\bin
use strict;
use warnings;

use Data::Dumper;

my @list = ( [ 1, 2, 3 ],
             [ 4, 5, 6 ],
             [ 7, 8, 9 ], );

             print Dumper \@list; 

my @other_list;
push ( @other_list, @list[0,1] ); #make a sub list of two rows;
print Dumper \@other_list; 


### all looks good.
## but if we:

print "List:\n";
print join ("\n",@list),"\n";
print "Other List:\n";
print join ("\n", @other_list),"\n";

$list[1][1] = 9;

print Dumper \@other_list;

您会看到,通过更改@list中的元素,我们也会修改@other_list - 如果我们只是print,我们会得到:

List:
ARRAY(0x2ea384)
ARRAY(0x12cef34)
ARRAY(0x12cf024)
Other List:
ARRAY(0x2ea384)
ARRAY(0x12cef34)

请注意重复的数字 - 这意味着您有相同的参考。

解决这个问题最简单的方法是司法使用[]

push ( @other_list, [@{$list[0]}], [@{$list[1]}] ); #make a sub list of two rows;

然后,这将插入包含列表的解除引用元素的匿名数组(新数组)。

虽然我们虽然参与其中 - 请启用strictwarnings。从长远来看,它们将为您节省很多痛苦。

答案 1 :(得分:2)

$j = $f{"rank"}{$r};
$p0e[$i] = $p[$j];

$p[$j]是一个数组引用,您可以将其视为指向特定内存地址的特定数据列表。对$p0e[$i]的赋值也告诉Perl让第$i@p0e也引用同一块内存。因此,当您稍后对$p0e[$i][$k]进行更改时,您会发现$p[$j][$k]的值也发生了变化。

要解决此问题,您需要指定$p[$j]副本。这是你可以做到的一种方式:

$p0e[$i] = [ @{$p[$j]} ];

@{$p[$j]}推断数组引用,[...]为它创建一个新引用,因此在此语句后$p0e[$i]将具有与$p[$j]相同的内容相同的内容但是指向不同的内存块。

答案 2 :(得分:0)

那是因为它是一个数组数组。第一级数组仅存储对内部数组的引用,如果修改内部数组,则它在两个数组中都会更改 - 它们都引用相同的数组。 Clone深层复制而不是创建浅层复制品。