【问题标题】:How to generate an array with a counter in Perl?如何在 Perl 中生成带有计数器的数组?
【发布时间】:2012-04-04 23:03:51
【问题描述】:

我想生成一个唯一 ID 列表。因为有些 ID 是重复的,所以我需要在末尾添加一个数字以使其唯一,如下所示:

ID=exon00001
ID=exon00002
ID=exon00003
ID=exon00004

这是我目前所拥有的。

 while (loop through the IDs) {
 # if $id is an exon, then increment the counter by one and add it 
 # to the end of the ID
    if ($id =~ m/exon/) {
    my $exon_count = 0;
    my @exon = $exon_count++; #3
    $number = pop @exon; # removes the first element of the list
    $id = $id.$number;
    print $id."/n"
    }
    }

基本上我想用计数器动态生成一个数组。它应该为外显子的总数创建一个数组 (1, 2, 3, 4, ...),然后删除元素并将其添加到字符串中。此代码无法正常工作。我认为第 3 行有问题。你们知道吗?有任何想法吗?谢谢

【问题讨论】:

  • 我的 $exon_count 在循环内并且每次都设置为零。将声明移动到循环之前。然后它将通过循环递增。另外,我会直接使用 $exon_count 而不是做所有的工作来将它分配给一个数组,然后将它放入数字中,或者只是使用数字并增加它。
  • 你的代码充满了错误,即使它可以编译,它也不会按照你的想法去做。例如:$exon_count 在每次找到新的外显子时都会重置,您将单个值(始终为 0,因为之后评估 ++)分配给数组,pop 删除 last array 的元素,"/n" 将打印一个斜杠和n,如果你想要换行,你需要"\n"
  • 为了补充这些家伙所说的,shift 从列表中删除了 first 元素,pop 删除了 last--然而它确实删除了堆栈的“顶部”元素,但这是一个 stack,而不是 list

标签: arrays perl while-loop unique counter


【解决方案1】:

这是你需要的吗?计数器需要保持它的值,所以你不能一直重置它:

use v5.10;

my $exon_count = 0;
while( my $id = <DATA> ) {
    chomp $id;
    if( $id =~ m/exon/ ) {
        $id = sprintf "%s.%03d", $id, $exon_count++;
        }
    say $id;
    }

__END__
ID=exon00001
ID=exon00002
ID=exon00003
ID=exon00004

输出如下:

ID=exon00001.000
ID=exon00002.001
ID=exon00003.002
ID=exon00004.003

如果您使用的是 5.10 或更高版本,则可以使用 state 在循环内声明变量,但让它保持其值:

use v5.10;

while( my $id = <DATA> ) {
    chomp $id;
    state $exon_count = 0;
    if( $id =~ m/exon/ ) {
        $id = sprintf "%s.%03d", $id, $exon_count++;
        }
    say $id;
    }

我认为你是 Perl 的新手,因为你的代码看起来像是一堆不相关的东西,它们的作用可能与你想象的大不相同。有一个针对生物学家的 Perl 教程,"Unix and Perl"。还有我的Learning Perl书。

Joel 询问有关使用字符串作为附加标记的问题。没关系; Perl 允许您increment a string,但仅限于a-zA-Z 范围内。我们可以通过使用以 36 为基数的数字标签来混合数字和字母:

use v5.10;

use Math::Base36 'encode_base36';

while( my $id = <DATA> ) {
    chomp $id;
    state $exon_count = 30;
    if( $id =~ m/exon/ ) {
        $id = sprintf "%s.%-5s", $id, encode_base36($exon_count++);
        }
    say $id;
    }

现在你有这样的标签:

ID=exon00003.1Q   
ID=exon00004.1R   
ID=exon00001.1S   
ID=exon00002.1T   
ID=exon00003.1U   
ID=exon00004.1V   

【讨论】:

  • 对使用神奇的$string++有什么想法吗?所有人都需要一个唯一的标识符。不是很需要,但很有趣。
  • @JoelBerger 您也可以使用范围运算符来执行此操作,例如for ('exon1' .. 'exon9') { print }。我不会向新用户推荐它,因为它也会增加字母,例如exon9 变成 exoo0,而不是人们想象的 exon10
【解决方案2】:

正如我在评论中所指出的,您的代码无法编译,也无法工作。首先计算重复次数,然后根据找到的 id 打印正确的重复次数。使用printf 将适合格式化您的号码。

my %seen;
my @ids = ( bunch of ids );

map $seen{$_}++, @ids;  # count the duplicates

for my $id (keys %seen) {
    for my $num (1 .. $seen{$id}) {
        printf "%s%05d\n", $id, $num;
    }
}

【讨论】:

    【解决方案3】:

    您想为这些外显子生成一个唯一 ID 列表(输出到 GFF 文件中?)。

    您必须确保在循环之外初始化计数器。我不确定你想用数组完成什么。但是,下面的程序会根据您发布的格式(exon00001 等)生成唯一的外显子 ID。

    my $exon_count=0;
    
    while(my $id=<SOMEINPUT>){
          if($id=~m/exon/){
                $exon_count++;
            my $num='0' x (5 - length $exon_count) . $exon_count;
                print "$id$num\n";
          }
    }
    

    【讨论】:

    • 填充一个数字需要做很多工作。 sprintf 为您做到这一点。 :)
    猜你喜欢
    • 2010-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-11
    • 2021-01-16
    • 2017-09-27
    • 1970-01-01
    相关资源
    最近更新 更多