我在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]);
}
}
}
}
我想也许我以某种方式创建了对原始数组的引用而不是副本,但是因为这种意外的行为在每次迭代中都不会发生,所以情况并非如此。
答案 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;
然后,这将插入包含列表的解除引用元素的匿名数组(新数组)。
虽然我们虽然参与其中 - 请启用strict
和warnings
。从长远来看,它们将为您节省很多痛苦。
答案 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深层复制而不是创建浅层复制品。