如何打破嵌套的until循环,而不影响它最初的for循环

时间:2016-08-17 20:13:33

标签: perl loops

perl遇到了非常技术性的编码问题。基本上我正在寻找一些特定的DNA序列。

my $seq = '...';
my @dna = split //, $seq;
my $amount = scalar @dna; 

for my $index (0 .. $amount - 1){
    if ($dna[$index] eq 'A' and $dna[$index+1] eq 'T' and 
         $dna[$index+2] eq 'G' and $dna[$index+3] eq 'C'
    ) {
        do {
            print $dna[$index++];
        } until ($dna[$index] eq 'C' and $dna[$index+1] eq 'C' and $dna[$index+2] eq'G')
    }
}

问题是,“until”部分是否处于无限循环中。我得到了所有序列,但由于某种原因,直到循环不会停止循环。我试过了

until ($dna[$index] eq 'C' and $dna[$index+1] eq 'C' and $dna[$index+2] eq 'G' ){last;}

但这突破了“for”循环。 有没有办法在不破坏 for 循环的情况下突破 do-until 循环?

3 个答案:

答案 0 :(得分:3)

<案例1
my $seq = 'xxxxxATGCyyyyyCCGzzzzz';  # Ouputs ATGCyyyyy

ATGC后面跟CCG后,您的代码就可以运行。

<案例2
my $seq = 'xxxxxATGCyyyyy';  # Infinite loop

如果CCG后面没有ATGC,则会$index越过@dna,导致无限循环。

<案例3
my $seq = 'wwwwwATGCxxxxxATGCyyyyyCCGzzzzz';  # Ouputs ATGCxxxxxATGCyyyyy and ATGCyyyyy

你开始寻找上一场比赛开始的另一场比赛,而不是结束,所以你最终可能会有重叠的比赛。部分原因是假设循环将从您更改的值$index继续。

解决方案

my $seq = '...';
my @seq = split //, $seq;
my $seq_len = @seq;
for (my $i = 0; $i < $seq_len-7; ++$i) {
    if (   $seq[$i+0] eq 'A'
        && $seq[$i+1] eq 'T'
        && $seq[$i+2] eq 'G'
        && $seq[$i+3] eq 'C'
     ) {
        my $start = $i;
        $i += 4;
        for (; $i < $seq_len-3; ++$i) {
            if (   $seq[$i+0] eq 'C'
                && $seq[$i+1] eq 'C'
                && $seq[$i+2] eq 'G'
            ) {
                my $end = $i;
                print(join('', @seq[$start .. $end-1]), "\n");
                last;
            }
        }
    }
}

substr简化了事情。

my $seq = '...';
my $seq_len = length($seq);
for (my $i = 0; $i < $seq_len-7; ++$i) {
    if (substr($seq, $i, 4) eq 'ATGC') {
        my $start = $i;
        $i += 4;
        for (; $i < $seq_len-3; ++$i) {
            if (substr($seq, $i, 3) eq 'CCG') {
                my $end = $i;
                print(substr($seq, $start, $end-$start), "\n");
                last;
            }
        }
    }
}

但正则表达式只是更进一步。

my $seq = '...';
while ($seq =~ / ( ATGC .*? ) (?= CCG ) /xsg) {
    print("$1\n");
}

如果您想在没有CCG时输出字符串的剩余部分,可以使用以下内容:

my $seq = '...';
while ($seq =~ / ( ATGC (?:(?! CCG ).)* ) /xsg) {
    print("$1\n");
}

答案 1 :(得分:2)

正如perlsyn

中所述
  

对于&#34; last&#34;,你必须更精细:

LOOP: { 
    do {
        last if $x = $y**2;
        # do something here
    } while $x++ <= $z;
}

如果序列在CCG之后的某个地方不包含ATGC,则循环不会终止。将or $index == $#dna添加到条件中。

答案 2 :(得分:0)

虽然我绝对赞同@ ikegami基于正则表达式的方法,但我们试着挽救你所拥有的东西。一些具体问题:

  • 使用C-style for / foreach循环,以便对$ index的调整转移到外循环
  • 您无法在lastnext循环中直接使用do {} until ()do {} while () - 这些不是while ()或{{1} }循环,它们是until ()构造,这些关键字不适用。有关将do构造包装在一次性循环中以使这些关键字可行,请参阅其他一些答案。
  • 在找到您要查找的内容之前,您需要处理数据耗尽的可能性。

这是我对你的代码进行的修改,让它运行并清除@ ikegami的测试障碍:

do

(是的,use constant { START => 'ATGC', STOP => 'CCG' }; use constant { START_LENGTH => length(START), STOP_LENGTH => length(STOP) }; my $sequence = 'wwwwwATGCxxxxxATGCyyyyyCCGzzzzz'; my @dna = split //, $sequence; for (my $index = 0; $index < @dna - START_LENGTH; $index++) { if (join('', @dna[$index .. $index + START_LENGTH - 1]) eq START) { do { print $dna[$index++]; } until ($index > $#dna or ($index < @dna - STOP_LENGTH and join('', @dna[$index .. $index + STOP_LENGTH - 1]) eq STOP)); print("\n"); } } 使用SO格式化程序,因为它认为它正在引入评论。我相信有人知道如何解决这个问题,并会相应地编辑这个答案。)