我最近正在开发一个很多项目,我正在重新编写代码以使其更加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的简化代码
实际问题
您可能已经注意到了
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 只是一个案例场景密钥代码。
答案 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
的arrayrefssub 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),对它的更改不会影响调用范围中具有相同名称的变量。