Perl:数组在传递给函数后变为空?

时间:2017-08-23 17:08:31

标签: arrays perl subroutine bioperl

我最近正在开发一个很多项目,我正在重新编写代码以使其更加OOP并将所有冗余代码传递到子程序中。

该脚本检查数据库中是否存在基因(通过各种方式)。它也可能报告可能的重复。在报告重复之前,该脚本确保它不是生物复制品#34; (基本上相同的生物学数据,但在基因组中具有不同的位置,因此,不是实际的重复)。为了做到这一点......

 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结果集,其中包含来自先前搜索的数据库命中,对于这种情况,它总是具有多于1个基因。 $id$spc$species_directory$dataset都是来自配置的字符串,并在此块上方定义。

solve_multihit子程序是一个相当复杂的函数,它试图解决多次命中是实际重复还是生物重复。请注意,我已将@gene_names@gene_ids数组传递给此函数。如果能够解决差异,该函数将返回正确基因的gene_id;或者如果不是0。可以在以下链接中找到sub的简化代码

https://codeshare.io/2EM8qN

实际问题

您可能已经注意到了

print STDERR "$id\tJ\tALERT CHECKSUM MULTI HIT\t(".join(",",@gene_names).")\n";

是在solve_multihit子程序调用之前和之后......根据STDERR,数组在运行函数后似乎变空了:

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{} sub的结果来解决它,但我想知道为什么它会变空。

PS:报告中的 J 只是一个案例场景密钥代码。

2 个答案:

答案 0 :(得分:2)

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的固定版本,但它有很多我无法解决的问题。

答案 1 :(得分:1)

我可以看到两种方法可以让您的代码完成数据删除工作。

@_中可用的函数参数对传递给它的数据具有别名。因此,如果您更改@_本身(或其元素),则更改函数外部的数据。

更有可能的是,当您通过引用传递时,您的sub可能直接使用它

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对它所使用的数组具有破坏性,这里通过奇怪的语法提供了一个由解除引用的@_参数构成的匿名数组,其中更改调用者中的数据。

splice没有理由做你做的事。它的目的来改变数组。

相反,使用传递给sub

的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),对它的更改不会影响调用范围中具有相同名称的变量。