簡體   English   中英

Perl:將數組傳遞給函數后變為空?

[英]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。 可在以下鏈接中找到該子的簡化代碼

https://codeshare.io/2EM8qN

實際問題

您可能已經注意到

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM