对数组的修改也会更改其他数组

Modifications to array also change other array

我在 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]);
            }
        }
    }
}

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

那是因为它是一个数组的数组。第一级数组只存储对内部数组的引用,如果你修改内部数组,它在两个数组中都会改变——它们都引用同一个数组。 Clone 深拷贝而不是创建浅拷贝。

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

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

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

问题是 - perl 'does' 多维数组的方式是通过引用数组。所以如果你复制一个内部数组,你通过引用来这样做。

例如:

#!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。在漫长的 运行 中,它们会为您省去很多痛苦。

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

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

要解决此问题,您需要分配 $p[$j]副本。这是您可以执行此操作的一种方法:

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

@{$p[$j]} 引用数组引用并且 [...] 为它创建一个新的引用,所以在这条语句之后 $p0e[$i] 将具有与 [=12= 相同的内容和相同的值] 但指向不同的内存块。