首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何以适当的方式使用until函数

如何以适当的方式使用until函数
EN

Stack Overflow用户
提问于 2014-04-07 02:17:49
回答 2查看 76关注 0票数 0

我有一个想要过滤的文件,如下所示:

代码语言:javascript
复制
##matrix=axtChain 16 91,-114,-31,-123,-114,100,-125,-31,-31,-125,100,-114,-123,-31,-114,91
##gapPenalties=axtChain O=400 E=30
chain 21455232 chr20 14302601 + 37457 14119338 chr22 14786829 + 3573 14759345 1
189     159     123
24      30      22
165     21      20
231     105     0
171     17      19
261     0       2231
222     2       0
253     56      48

chain 164224 chr20 14302601 + 1105938 1125118 chr22 14786829 + 1081744 1100586 8
221     352     334
24      100     112
34      56      56
26      50      47
…………………….
chain 143824 chr20 14302601 + 1105938 1125118 chr22 14786829 + 1081744 1100586 8

因此,简单地说,有一个空行分隔的块。每个块都以行“chain xxxxx”开始,并以带数字的行继续。我想过滤掉文件,只保留带链的块,后面的数字大于3000。为此,我编写了以下脚本:

代码语言:javascript
复制
#!/usr/bin/perl
use strict;
use warnings;
use POSIX;

my $chain = $ARGV[0];

#It filters the chains with chains >= 3000.
open my $chain_file, $chain or die "Could not open $chain: $!";

my @array;
while( my $cline = <$chain_file>)  {
    #next if /^\s*#/;
    chomp $cline;
    #my @lines = split (/ /, $cline);
    if ($cline =~/^chain/) {
        my @lines = split (/\s/, $cline);
        if ($lines[1] >= 3000) {
            #print $lines[1];
            #my @lines = split (/ /, $cline);

            #print "$cline\n";
            push (@array, $cline);
        }
    }
    until ($cline ne ' ') {
        push (@array, $cline);
    }


    foreach (@array) {
        print "$_\n";

    }
    undef(@array);
}

问题是我只能打印标题(链XXXXX…..)而不是每个区块下一行后面的数字。我使用until函数,直到会找到空行,但它不起作用。如果有人能帮我弄一下那个…。提前谢谢你,Vasilis。

EN

回答 2

Stack Overflow用户

发布于 2014-04-07 02:50:59

这里的第一个问题是' '是一个空格,而不是空行(""''应该没问题,因为您已经chomp-ed了该行。

第二个问题是

代码语言:javascript
复制
until ( $cline ne "" )

等同于

代码语言:javascript
复制
while ( $cline eq "" )

这与您将行推送到@array所需的内容相反。

也就是说,触发器运算符可能更适合您想要的东西:

代码语言:javascript
复制
my @array;
while ( <$chain_file> ) {          # Using $_ instead of $cline

    chomp;

    if ( do { /^chain\s+(\d+)/ && $1 >= 3000 } .. /^$/ ) {

                                   # Accumulate lines in @array
        push @array, $_;           # False until LHS evaluates to true ...
    }                              # ... then true until RHS evaluates to true

    else {
        for ( @array ) {
          print $_, "\n";          # Print matches
        }
        @array = ();               # Reset/clear out @array
    }
}
票数 1
EN

Stack Overflow用户

发布于 2014-04-07 04:03:22

通常情况下,最好不要使用unless而不是while。它多次否定布尔表达式,给您留下了一个双重否定的问题要解决。下面是一个例子

代码语言:javascript
复制
 while ( $foo ne $bar ) {

即使这是一个否定的表达式,我也可以很容易地确定何时退出我的循环。但是:

代码语言:javascript
复制
until ( $foo eq $bar ) {

只是需要时间来弄清楚。

此外,‘’不会生成空行:请使用正则表达式$cline =~ /^\s*$/。然而,即使在此基础上,循环:

代码语言:javascript
复制
until ($cline ne ' ') {
    push (@array, $cline);
}

如果$cline等于空,它将永远运行。您永远不会更改$cline的值。

您可以使用我用来称为状态变量的方法(直到Perl实际创建了一个名为state的变量类型,而现在我不知道该如何调用它们)。此变量跟踪您在文件中的位置。你是在文件的链节里吗?你到底想不想要这些行?这样,您只有一个循环。您设置状态变量,然后处理您的循环。

在本例中,我有一个名为$keep_line的状态变量,它询问我是否希望保留要读入的行。如果行以chain开头,并且第二个字段大于3000,我希望保留整个块(如果我理解您要做什么的话)。(顺便说一句,我保留了空行。这样可以吗?)

代码语言:javascript
复制
my $keep_lines = 0;   # Aren't in lines I want to keep
my @good_lines;       # Array where you want to keep the good lines

while ( my $line = <$chain_file> ) {
     chomp $line;                   # Good habit to always chomp a input as soon as it's read.
     if ( $line =~ /^chain/ ) {     # This is a chain line. Do I want to keep this group?
          my @fields =  ( /\s+/, $line );
         if ( $field[1] > 3000 ) {  # Yes, if the second field is greater than 3000
             $keep_lines = 1;       # Keep this and all other lines until the next chain line
         }
         else {
             $keep_lines = 0;       # Skip until the next chain line
         }
     }
     if ( $keep_lines ) {
         push @good_lines, $line;
     }
}

代码语言:javascript
复制
while ( my $line = <$chain_file> ) {
     chomp $line;                   # Good habit to always chomp a input as soon as it's read.
     $keep_lines = keep_this_section( $line );
     if ( $keep_lines ) {
         push @good_lines, $line;
     }
}

大大简化了代码。

我还会声明一些常量来删除那些神秘的Moes。像3000/^chain/这样的东西在你的程序中有着神秘而又重要的意义。您可以使用use constant作为编译指示来定义Perl常量。它是标准Perl的一部分:

代码语言:javascript
复制
use constant {
    KEEP_LIMIT       => 3000,
    SECTION_HEADER   => qr/^chain/,
};

现在,我可以这样做:

代码语言:javascript
复制
if ( $line =~ SECTION_HEADER ) {

而不是:

代码语言:javascript
复制
if ( $line =~ /^chain/ ) {

代码语言:javascript
复制
if ( $field[1] > KEEP_LIMIT ) {

而不是

代码语言:javascript
复制
if ( $field[1] > 3000 ) {

constant杂注有问题。最大的问题是,它不会在Perl通常会对变量进行插值的地方进行插值。这包括双引号字符串和散列键。如果我有$foo{KEEP_LIMIT},Perl会将键解释为字符串KEEP_LIMIT,而不是KEEP_LIMIT的常量。

许多开发人员使用Readonly,它在很多方面都要好得多。所以,我倾向于使用constant

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22897997

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档