gpt4 book ai didi

arrays - 如何在 Perl 中正确替换哈希数组中的值?

转载 作者:行者123 更新时间:2023-12-02 01:57:41 24 4
gpt4 key购买 nike

如下所示,我有一个 foreach 循环,其中哈希数组中的值将被另一个哈希数组中的值替换。

第二个 foreach 循环只是打印并测试值是否分配正确。

foreach my $row (0 .. $#row_buff) {
$row_buff[$row]{'offset'} = $vars[$row]{'expression'};
print $row_buff[$row]{'offset'},"\n";
}

foreach (0 .. $#row_buff) {
print $row_buff[$_]{'offset'},"\n";
}

这里@row_buff和@vars是两个哈希数组。它们预先填充了所有使用的键的值。

哈希值被插入数组,如下所示:

push @row_buff, \%hash;

问题:假设第一个 foreach 打印中的打印语句如下所示:

string_a
string_b
string_c
string_d

然后第二个 foreach 循环中的打印语句如下所示:

string_d
string_d
string_d
string_d

这就是我困惑的地方。两个打印语句应该以完全相同的方式打印,对吗?但是第二个打印语句打印的值只是重复方式的最后一个值。有人可以指出我这里可能出了什么问题吗?任何提示都将不胜感激。这是我第一次提出问题,如果我遗漏了什么,请原谅我。

更新

我可以添加一些信息,对此大家表示歉意。在 foreach 之前还有一行,就像这样:

@row_buff = (@row_buff) x $itercnt;
foreach my $row (0 .. $#row_buff) {
$row_buff[$row]{'offset'} = $vars[$row]{'expression'};
print $row_buff[$row]{'offset'},"\n";
}

foreach (0 .. $#row_buff) {
print $row_buff[$_]{'offset'},"\n";
}

$itercnt 是一个整数。我用它多次复制 @row_buff。

最佳答案

这显然与在数组上存储引用有关,而不是独立数据。由于没有给出详细信息,因此尚不清楚这是如何发生的,但以下讨论应该会有所帮助。

考虑这两个基本示例。

首先,在数组上放置一个散列(引用),每次首先更改一个值

use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd);
# use Storable qw(dclone);

my %h = ( a => 1, b => 2 );

my @ary_w_refs;

for my $i (1..3) {
$h{a} = $i;
push @ary_w_refs, \%h; # almost certainly WRONG

# push @ary_w_refs, { %h }; # *copy* data
# push @ary_w_refs, dclone \%h; # may be necessary, or just safer
}

dd $_ for @ary_w_refs;

我使用Data::Dump用于显示复杂的数据结构,因其简单性和默认的紧凑输出。还有其他用于此目的的模块,Data::Dumper位于核心(已安装)。

以上打印

{ a => 3, b => 2 }{ a => 3, b => 2 }{ a => 3, b => 2 }

See how that value for key a, that we changed in the hash each time, and so supposedly set for each array element, to a different value (1, 2, 3) -- is the same in the end, and equal to the one we assigned last? (This appears to be the case in the question.)

This is because we assigned a reference to the hash %h to each element, so even though every time through the loop we first change the value in the hash for that key in the end it's just the reference there, at each element, to that same hash.

So when the array is queried after the loop we can only get what is in the hash (at key a it's the last assigned number, 3). The array doesn't have its own data, only a pointer to hash's data. (Thus hash's data can be changed by writing to the array as well, as seen in the example below.)

Most of the time, we want a separate, independent copy. Solution? Copy the data.

Naively, instead of

push @ary_w_refs, \%h;

我们能做到

push @ary_w_refs, { %h };

此处 {} 是匿名哈希的构造函数, 因此内部的 %h 被复制。那么实际数据进入数组后一切都很好吗?在本例中,是的,其中哈希值是纯字符串/数字。

但是当哈希值本身是引用时怎么办?然后这些引用被复制,并且 @ary_w_refs 再次没有自己的数据!我们会遇到完全相同的问题。 (尝试上面的哈希值 ( a => [1..10] ))

如果我们有一个复杂的数据结构,包含值的引用,我们需要一个深拷贝。一种好方法是使用库,并且 Storabledclone非常好

use Storable qw(dclone);
...

push @ary_w_refs, dclone \%h;

现在数组元素拥有自己的数据,与 %h 无关(但在复制时相等)。

对于简单的哈希/数组来说,这也是一件好事,可以避免将来的更改,即哈希已更改,但我们忘记了它被复制的位置(或者哈希及其副本没有更改)甚至互相了解)。

另一个例子。让我们用 hashref 填充一个数组,然后将其复制到另一个数组

use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd pp);

my %h = ( a => 1, b => 2 );

my @ary_src = \%h;
say "Source array: ", pp \@ary_src;

my @ary_tgt = $ary_src[0];
say "Target array: ", pp \@ary_tgt;

$h{a} = 10;
say "Target array: ", pp(\@ary_tgt), " (after hash change)";

$ary_src[0]{b} = 20;
say "Target array: ", pp(\@ary_tgt), " (after hash change)";

$ary_tgt[0]{a} = 100;
dd \%h;

(为了简单起见,我使用只有一个元素的数组。)

这会打印

Source array: [{ a => 1, b => 2 }]Target array: [{ a => 1, b => 2 }]Target array: [{ a => 10, b => 2 }] (after hash change)Target array: [{ a => 10, b => 20 }] (after hash change){ a => 100, b => 20 }

That "target" array, which supposedly was merely copied off of a source array, changes when the distant hash changes! And when its source array changes. Again, it is because a reference to the hash gets copied, first to one array and then to the other.

In order to get independent data copies, again, copy the data, each time. I'd again advise to be on the safe side and use Storable::dclone (or an equivalent library of course), even with simple hashes and arrays.

Finally, note a slightly sinister last case -- writing to that array changes the hash! This (second-copied) array may be far removed from the hash, in a function (in another module) that the hash doesn't even know of. This kind of an error can be a source of really hidden bugs.

Now if you clarify where references get copied, with a more complete (simple) representation of your problem, we can offer a more specific remedy.


An important way of using a reference that is correct, and which is often used, is when the structure taken the reference of is declared as a lexical variable every time through

for my $elem (@data) { 
my %h = ...
...
push @results, \%h; # all good
}

每次都会重新引入词法 %h,以便保留其在数组上的引用数据,因为数组在循环之外持续存在,对于每个元素都是独立的。

这样做的效率也更高,因为 %h 中的数据不会像 { %h } 那样被复制,而只是“重新调整用途”,也就是说,从在迭代结束时被销毁的词法 %h 到数组中的引用。

如果要复制的结构自然位于循环之外,这当然可能并不总是合适。然后使用它的深拷贝。

函数调用中也有同样的机制

sub some_func {
...
my %h = ...
...
return \%h; # good
}

my $hashref = some_func();

同样,当函数返回时,词法 %h 超出了范围,并且它不再存在,但它携带的数据和对它的引用被保留,因为它被返回并进行分配,使其引用计数非零。 (至少返回给调用者,也就是说;它可能在子执行过程中被传递到其他地方,所以我们可能仍然会遇到多个使用相同引用的 Actor 的困惑。)所以$hashref 具有对子中创建的数据的引用。

回想一下,如果一个函数被传递了一个引用,那么当它被调用时或在其执行期间(通过调用其他返回引用的子函数),更改并返回它,那么我们再次在某些调用者中更改了数据,可能会被远远删除从这部分程序流程来看。

当然,这种情况经常发生,因为数据池较大,不能一直复制,但是需要小心并组织代码(例如,尽可能模块化),以便最大限度地减少出错的可能性。

这是对“指针”一词的宽松使用,用于表示引用的作用,但如果要引用 C,我会说它有点“修饰” C指针

在不同的上下文中它可以是一个 block

关于arrays - 如何在 Perl 中正确替换哈希数组中的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69412285/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com