[英]Perl: array goes empty after passing it to a function?
最近,我正在開發一個規模很大的項目,並且我正在重新編寫代碼以使其更具面向對象性,並將所有冗余代碼傳遞給子例程。
該腳本檢查基因是否存在於數據庫中(通過各種方式)。 它還可能報告可能的重復項。 在報告重復之前,腳本要確保它不是“生物學重復”(本質上是相同的生物學數據,但是在基因組中具有不同的位置,因此不是實際的重復)。 為了這樣做...
my @gene_ids;
my @gene_names;
while(my $gene = $geners_bychecksum->next){
my $gene_name = $gene->gene_name;
my $gene_id = $gene->gene_id;
push @gene_ids, $gene_id;
push @gene_names, $gene_name;
}
print STDERR "$id\tJ\tALERT CHECKSUM MULTI-HIT\t(".join(",",@gene_names).")\n";
my $solve_multihit = solve_multihit($id, \@gene_names, \@gene_ids, $spc, $species_directory, $dataset);
print STDERR "$id\tJ\tALERT CHECKSUM MULTI-HIT\t(".join(",",@gene_names).")\n";
if($solve_multihit){
print STDERR "$id\tM\tUPDATE \n";
print $report "$id\tM\tUPDATE \n";
$countM++;
} else {
print STDERR "$id\tJ\tALERT CHECKSUM MULTI-HIT\t(".join(",",@gene_names).")\n";
}
在這里, $geners_bychecksum
是一個DBIC重新定義的集合,具有來自先前搜索的數據庫命中,並且在這種情況下,它始終具有多個基因。 $id
, $spc
, $species_directory
和$dataset
都是來自配置的字符串,並在此塊上$species_directory
。
solve_multihit
子例程是一個相當復雜的函數,它試圖確定多重匹配是實際重復還是生物學重復。 請注意,我正在將@gene_names
和@gene_ids
數組傳遞給此函數。 如果能夠解決差異,則此函數將返回適當基因的gene_id。 否則為0。 可在以下鏈接中找到該子的簡化代碼
實際問題
您可能已經注意到
print STDERR "$id\\tJ\\tALERT CHECKSUM MULTI HIT\\t(".join(",",@gene_names).")\\n";
根據STDERR,它是在solve_multihit
子例程調用之前和之后的,並且該數組在運行該函數后似乎為空:
BBOV_I005030 J ALERT CHECKSUM MULTI-HIT (XP_001609152.1,XP_001609157.1)
BBOV_I005030 J ALERT CHECKSUM MULTI-HIT ()
BBOV_I005040 J ALERT CHECKSUM MULTI-HIT (XP_001609156.1,XP_001609153.1)
BBOV_I005040 J ALERT CHECKSUM MULTI-HIT ()
BBOV_I005050 J ALERT CHECKSUM MULTI-HIT (XP_001609154.1,XP_001609155.1)
BBOV_I005050 J ALERT CHECKSUM MULTI-HIT ()
BBOV_I005060 J ALERT CHECKSUM MULTI-HIT (XP_001609154.1,XP_001609155.1)
BBOV_I005060 J ALERT CHECKSUM MULTI-HIT ()
BBOV_I005070 J ALERT CHECKSUM MULTI-HIT (XP_001609156.1,XP_001609153.1)
BBOV_I005070 J ALERT CHECKSUM MULTI-HIT ()
BBOV_I005080 J ALERT CHECKSUM MULTI-HIT (XP_001609152.1,XP_001609157.1)
BBOV_I005080 J ALERT CHECKSUM MULTI-HIT ()
為什么會這樣? 我很確定我可以通過返回數組以及solve_multihit{}
子程序的結果來解決它,但是我想知道為什么它會變空。
PS:報告中的J只是一個案例場景的關鍵代碼。
my @gene_names = splice(shift);
my @gene_ids = splice(shift);
是短的
my @gene_names = splice(@{ shift(@_) });
my @gene_ids = splice(@{ shift(@_) });
splice(@a)
清空數組並返回其內容。 沒有理由這樣做! 以上應該是
my @gene_names = @{ shift(@_) };
my @gene_ids = @{ shift(@_) };
老實說,不需要復制數組。 只需使用提供的參考。
my $gene_names = shift;
my $gene_ids = shift;
我將提供一個solve_multihit
- solve_multihit
的固定版本,但是它有很多主要問題,我無法使用我所擁有的信息來解決。
我可以看到您的代碼完成似乎正在執行的數據刪除的兩種方法。
@_
中可用的函數參數是傳遞給它的數據的別名。 因此,如果更改@_
本身(或其元素),則會在函數外部更改數據。
當您通過引用傳遞時,您的潛艇更有可能直接與其一起使用
sub ff {
my ($rary) = @_;
@$rary = ();
}
my @data = 1..4;
ff(\@data);
say for @data; # empty
如果您的處理需要更改與之配合使用的數組,請先制作本地副本
sub ff {
my ($rary) = @_;
my @local_ary = @$ary;
# now changes to @local_ary do not affect @data in the caller
}
這通常更安全,盡管它確實引入了數據副本,但使用參考時不會發生這種情況。
該編輯以及ikegami的答案可以解決此問題: 拼接對與其使用的數組具有破壞性,在這里,由於奇怪的語法,它將由取消引用的@_
參數形成的匿名數組饋入,從而更改了調用者中的數據。
您所做的工作沒有理由進行splice
。 其目的是更改數組。
而是使用傳遞給子對象的arrayrefs
sub solve_multihit {
my ($id, $gene_names, $gene_ids, ...) = @_;
foreach my $name (@$gene_names) {
...
}
...
}
或根據需要制作本地副本
sub solve_multihit {
my $id = shift;
my @gene_names = @{ shift @_ };
...
}
其中, my @gene_names
是此作用域中的一個詞法變量(在您的情況下為sub),對其進行更改不會影響調用范圍中具有相同名稱的變量。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.