如下所示,我有一個 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 是兩個哈希陣列。它們預先填充了所有使用的鍵的值。
散列被推入陣列,如下所示:
推@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";
}
$itrcnt 是一個整數。我用它來多次復制@row_buff。
uj5u.com熱心網友回復:
這顯然與在陣列上存盤參考有關,而不是與獨立資料有關。由于未提供詳細資訊,因此不清楚這是如何發生的,但以下討論應該會有所幫助。
考慮這兩個基本示例。
首先,在陣列上放置一個散列(參考),每次先改變一個值
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 }
看看 key 的值如何,a我們每次都在散列中更改,因此應該為每個陣列元素設定不同的值 ( 1, 2, 3) -- 最后是相同的,并且等于我們最后分配的值? (這似乎是問題中的情況。)
這是因為我們為每個元素分配了對散列的參考%h,所以即使每次通過回圈我們首先更改該鍵的散列中的值,最后它只是那里的參考,在每個元素上,到相同的散列. *
因此,當在回圈之后查詢陣列時,我們只能獲取哈希中的內容(關鍵是a它是最后分配的數字,3)。該陣列沒有自己的資料,只有一個指向哈希資料的指標。?(因此也可以通過寫入陣列來更改哈希資料,如下例所示。)
大多數時候,我們想要一個單獨的、獨立的副本。解決方案?復制資料。
天真地,而不是
push @ary_w_refs, \%h;
我們可以做的
push @ary_w_refs, { %h };
Here {} is a constructor for an anonymous hash,? so %h inside gets copied. So actual data gets into the array and all is well? In this case, yes, where hash values are plain strings/numbers.
But what when the hash values themselves are references? Then those references get copied, and @ary_w_refs again does not have its own data! We'll have the exact same problem. (Try the above with the hash being ( a => [1..10] ))
If we have a complex data structure, carrying references for values, we need a deep copy. One good way to do that is to use a library, and Storable with its dclone is very good
use Storable qw(dclone);
...
push @ary_w_refs, dclone \%h;
Now array elements have their own data, unrelated (but at the time of copy equal) to %h.
This is a good thing to do with a simple hash/array as well, to be safe from future changes, whereby the hash is changed but we forget about the places where it's copied (or the hash and its copies don't even know about each other).
Another example. Let's populate an array with a hashref, and then copy it to another array
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;
(For simplicity I use arrays with only one element.)
This prints
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
}
That lexical %h is introduced anew every time so the data for its reference on the array is retained, as the array persists beyond the loop, independently for each element.
It is also more efficient doing it this way since the data in %h isn't copied, like it is with { %h }, but is just "re-purposed," so to say, from the lexical %h that gets destroyed at the end of iteration to the reference in the array.
This of course may not always be suitable, if a structure to be copied naturally lives outside of the loop. Then use a deep copy of it.
The same kind of a mechanism works in a function call
sub some_func {
...
my %h = ...
...
return \%h; # good
}
my $hashref = some_func();
Again, the lexical %h goes out of scope as the function returns and it doesn't exist any more, but the data it carried and a reference to it is preserved, since it is returned and assigned so its refcount is non-zero. (At least returned to the caller, that is; it could've been passed yet elsewhere during the sub's execution so we may still have a mess with multiple actors working with the same reference.) So $hashref has a reference to data that had been created in the sub.
Recall that if a function was passed a reference, when it was called or during its execution (by calling yet other subs which return references), changed and returned it, then again we have data changed in some caller, potentially far removed from this part of program flow.
This is done often of course, with larger pools of data which can't just be copied around all the time, but then one need be careful and organize code (to be as modular as possible, for one) so to minimize chance of errors.
? This is a loose use of the word "pointer," for what a reference does, but if one were to refer to C I'd say that it's a bit of a "dressed" C-pointer
? In a different context it can be a block
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/316271.html
