【问题标题】:perl - How to replace an empty value in an array without using a variable for comparison?perl - 如何在不使用变量进行比较的情况下替换数组中的空值?
【发布时间】:2017-12-11 19:16:30
【问题描述】:

我有一个数组,其中每个元素都来自制表符分隔的行。

初始代码:

#!/usr/bin/perl -w
use strict;

下面的代码是一段代码。

sub parser_domains {

my @params = @_;

my $interpro_line = "";
my @interpro_vector = ( );
my $idr_sub_id = $params[0];
my $idr_sub_start = $params[1]+1;
my $idr_sub_end = $params[2]+1;
my $interpro_id = "";
my $interpro_start_location = 0;
my $interpro_end_location = 0;
my $interpro_db = "";
my $interpro_length = 0;
my $interpro_db_accession = "";
my $interpro_signature = "";
my $interpro_evalue = "";
my $interpro_vector_size = 0;
my $interpro_sub_file= "";
my $idr_sub_lenght = ($idr_sub_end-$idr_sub_start)+1;

$interpro_sub_file = "$result_directory_predictor/"."$idr_sub_id/"."$idr_sub_id".".fsa.tsv";

#open file; if it fails, print a error and exits.
unless( open(TSV_FILE_DATA, $interpro_sub_file) ) {
        print "Cannot open file \"$interpro_sub_file\"\n\n";
        return;
}
my @interpro_file_line = <TSV_FILE_DATA>;
close TSV_FILE_DATA;

foreach $interpro_line (@interpro_file_line) {
    @interpro_vector = split('\t',$interpro_line);
    $interpro_id = $interpro_vector[0];
    $interpro_db = $interpro_vector[3];
    $interpro_db_accession = $interpro_vector[4];
    $interpro_start_location = $interpro_vector[6];
    $interpro_end_location = $interpro_vector[7];
    $interpro_signature = $interpro_vector[11];
    $interpro_length = ($interpro_end_location-$interpro_start_location) + 1;

    if ($interpro_signature eq ""){

            $interpro_signature = "NOPIR";
            printf IDP_REGION_FILE "\nFound a $interpro_db domain with no IPR: starts at $interpro_start_location and ends at $interpro_end_location\n";
            printf IDP_REGION_FILE "The size of $interpro_db domain in the sequence is $interpro_length\n";
            printf IDP_REGION_FILE "The IDR starts at $idr_sub_start and and ends at $idr_sub_end\n";
            printf IDP_REGION_FILE "The size of IDR is $idr_sub_lenght\n";
            domains_regions($idr_sub_start,$idr_sub_end,$interpro_start_location,$interpro_end_location,$interpro_signature,$interpro_length,$interpro_db,$idr_sub_id,$interpro_db_accession,$idr_sub_lenght);
    }
    else{
        for $entry_line (@entry_file_line) {
            @entry_vector =  split('\t',$entry_line);
            $entry_ac = $entry_vector[0];
            $entry_type = $entry_vector[1];
            $entry_name = $entry_vector[2];
            chomp($entry_name);

            if ($interpro_signature eq $entry_ac) {
                printf IDP_REGION_FILE "\nFound a $interpro_db domain with Interpro Signature $entry_ac: starts at $interpro_start_location and ends at $interpro_end_location\n";
                printf IDP_REGION_FILE "The size of $interpro_db domain in the sequence is $interpro_length\n";
                printf IDP_REGION_FILE "The Interpro Signature $entry_ac belongs to type $entry_type\n";
                printf IDP_REGION_FILE "The name of $entry_ac is $entry_name\n";
                printf IDP_REGION_FILE "The IDR starts at $idr_sub_start and ends at $idr_sub_end\n";
                printf IDP_REGION_FILE "The size of IDR is $idr_sub_lenght\n";  

                domains_regions($idr_sub_start,$idr_sub_end,$interpro_start_location,$interpro_end_location,$interpro_signature,$interpro_length,$interpro_db,$idr_sub_id,$interpro_db_accession,$idr_sub_lenght);
            }
        }
    }
}
}

tsv文件示例(interproscan):

P51587  14086411a2cdf1c4cba63020e1622579    3418    Pfam    PF09103 BRCA2, oligonucleotide/oligosaccharide-binding, domain 1    2670    2799    7.9E-43 T   15-03-2013
P51587  14086411a2cdf1c4cba63020e1622579    3418    ProSiteProfiles PS50138 BRCA2 repeat profile.   1002    1036    0.0 T   18-03-2013  IPR002093   BRCA2 repeat    GO:0005515|GO:0006302
P51587  14086411a2cdf1c4cba63020e1622579    3418    Gene3D  G3DSA:2.40.50.140       2966    3051    3.1E-52 T   15-03-2013
...

脚本完美运行,但比较 $interpro_signature eq "" 提供警告。

Use of uninitialized value $interpro_signature in string eq at /home/duca/eclipse-workspace/idps/idp_parser_interpro.pl line 666.

所以,我在比较之前搜索并尝试了将空值替换为数组的方式。我想要“NOIPR”的空值。 我正在处理 9 个完整的基因组,我有超过 324000 种蛋白质要解析。

如何替换数组中的空值?

谢谢。

【问题讨论】:

  • 好吧,得到Global symbol "$interpro_line" requires explicit package name (did you forget to declare "my $interpro_line"?) at so.pl line 6. so.pl had compilation errors.$interpro_line是什么?声明后,它是否包含至少 12 个制表符分隔值?你访问$interpro_vector[11]
  • $interpro_signature = $interpro_vector[11] // 'some_default_value'; // 是“定义或”运算符。并且请发布完整的代码,特别是如果您显示use ... - 这意味着代码应该运行。
  • @PerlDuck 声明了“$interpro_line”,它不是全局的。我现在发布子程序的整个代码。
  • @zdim 来自 Interproscan 结果,$interpro_vector[11] 可能为空或 tsv 中的特定值。 github.com/ebi-pf-team/interproscan/wiki/OutputFormats
  • 文件句柄IDP_REGION_FILE 在写信之前似乎还没有提到? (你说“整个代码”......这是一个错误吗?)还是我在某个地方错过了它?

标签: arrays perl suppress-warnings is-empty


【解决方案1】:

您的数组可能没有 12 个元素(或者第 12 个元素可能是 undef

my $interpro_signature = $interpro_vector[11] // 'some_default_value'; 

//defined-or operator

错误Use of uninitialized value表示该变量尚未初始化,或者已设置为undef

请参阅perldiag 并定期使用它。定期运行带有perl -Mdiagnostics ... 的代码。

use warnings; 实际上比-w 好。


更新问题的实质性编辑

  • 从显示的数据看来,文件中可能还没有给出其他字段;所以用默认值证明所有变量,就像上面索引11 处的数组元素一样。无论如何,这通常是您想要做的。例如,如果文件中有所有字段但有些可能是空的(两个选项卡之间没有任何内容)

    my @interpro_defaults = ('id_default', 'db_default', ...);
    
    my ($interpro_id, $interpro_db, ...) = 
        map { 
            $interpro_vector[$_] // $interpro_defaults[$_] 
        } 0 .. $#interpro_defaults;
    

    这依赖于列表中(变量的)顺序,变量容易出错;见下文。 如果某些领域根本不存在,那么可能还有(远)更多工作要做。

  • 有太多单独的变量,所有相关的并命名为$interpro_X(还有$idr_Y$entry_Z,但更少,也许可以管理)。

您不能将它们捆绑在容器类型的变量或数据结构中吗?

散列%interpro 似乎很合适,键为X(所以,$interpro{id} 等)。然后您可以更轻松地使用它们,并且可以对整个批次执行一些操作。初始化时您仍然需要注意顺序,因为它们是按顺序读取的,但这样应该更清楚。例如

my @interpro_vars   = qw(id db db_accesssion ...);
my @interpro_vector = qw(id_default db_default ...);
my %interpro;
@interpro{@interpro_vars} = @interpro_vector;
# or simply
@interpro{qw(id db ...)} = qw(id_default db_default ...);

我首先定义了带有键和值的数组,然后使用它们,以防您以后可能希望将这些列表放入数组中。如果不是这种情况,您可以使用列表(最后一行)初始化哈希。

这里

my %h; 
@h{LIST-keys} = LIST-values;

是一种将LIST-values 列表分配给LIST-keys 中给出的散列%h 的键集的方法。它们按照两个列表的给定顺序一对一分配(大小最好匹配)。在哈希键的前面有@ 印记,因为我们那里有一个(键)列表,而不是哈希。请注意,哈希必须已在某处声明。见slices in perldata

【讨论】:

  • 起初我也偶然发现了丢失的use warnings;,但后来在shebang行中看到了-w。这并不完美,但总比没有警告要好。
  • @euduca 更新了答案
  • @ikegami 感谢您的修复。我应该改掉那个“已定义”的坏习惯
  • @PerlDuck 谢谢你的链接;用它更新文本(按照我喜欢的方式)
  • 嗨@PerlDuck,我使用了 defined-or 运算符。警告消失了。当我使用 split 函数时,我了解了空值。谢谢。
【解决方案2】:

问题是您的第 3 行仅包含 9 个元素。所以

@interpro_vector = split('\t',$interpro_line);

因为该行仅将 9 个元素分配给 @interpro_vector,但您随后访问 $interpro_vector[11](即第 12 个元素)并且不存在。您现在可以检查 @interpro_vector 是否包含(至少)12 个元素:

if (@interpro_vector >= 12) {
    ...
}

或者您可以使用defined-or operator 作为@zdim suggested 来使用默认值,以防$interpro_vector[11] 未定义:

$interpro_signature = $interpro_vector[11] // '';

以上行等价于

if (defined $interpro_vector[11]) {
    $interpro_signature = $interpro_vector[11];
} else {
    $interpro_signature = '';
}

现在

if ($interpro_signature eq "") {
    ...
}

会起作用的。

【讨论】:

    猜你喜欢
    • 2020-05-30
    • 2020-01-02
    • 1970-01-01
    • 2022-06-11
    • 1970-01-01
    • 2010-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多