В Perl, как вы находите положение совпадения в строке, если вынуждены использовать цикл foreach? позиция - PullRequest
4 голосов
/ 01 февраля 2011

Мне нужно найти все позиции совпадающих строк в большей строке, используя цикл while, а в качестве второго метода - цикл foreach.Я понял метод цикла while, но я застрял на методе foreach.Вот метод 'while':

....

my $sequence = 
   'AACAAATTGAAACAATAAACAGAAACAAAAATGGATGCGATCAAGAAAAAGATGC'.
   'AGGCGATGAAAATCGAGAAGGATAACGCTCTCGATCGAGCCGATGCCGCGGAAGA'.
   'AAAAGTACGTCAAATGACGGAAAAGTTGGAACGAATCGAGGAAGAACTACGTGAT'.
   'ACCCAGAAAAAGATGATGCNAACTGAAAATGATTTAGATAAAGCACAGGAAGATT'.
   'TATCTGTTGCAAATACCAACTTGGAAGATAAGGAAAAGAAAGTTCAAGAGGCGGA'.
   'GGCTGAGGTAGCANCCCTGAATCGTCGTATGACACTTCTGGAAGAGGAATTGGAA'.
   'CGAGCTGAGGAACGTTTGAAGATTGCAACGGATAAATTGGAAGAAGCAACACATA'.
   'CAGCTGATGAATCTGAACGTGTTCGCNAGGTTATGGAAA';

my $string = <STDIN>;
chomp $string;

while ($sequence =~ /$string/gi )
{
 printf "Sequence found at position: %d\n", pos($sequence)- length($string);
}

Вот мой метод foreach:

foreach  ($sequence =~ /$string/gi ) 

 printf "Sequence found at position: %d\n", pos($sequence) - length($string); 
}

Может кто-нибудь дать мне подсказкупочему это не работает так же?Спасибо!

Мой вывод, если я введу "aaca":

Part 1 using a while loop
Sequence found at position: 0
Sequence found at position: 10
Sequence found at position: 17
Sequence found at position: 23
Sequence found at position: 377

Part 2 using a foreach loop
Sequence found at position: -4
Sequence found at position: -4
Sequence found at position: -4
Sequence found at position: -4
Sequence found at position: -4

Ответы [ 2 ]

10 голосов
/ 01 февраля 2011

Ваша проблема здесь в контексте.В цикле while условие находится в скалярном контексте.В скалярном контексте оператор сопоставления в режиме g будет последовательно сопоставляться вдоль строки.Таким образом, проверка pos внутри цикла делает то, что вы хотите.

В цикле foreach условие находится в контексте списка.В контексте списка оператор совпадений в режиме g вернет список всех совпадений (и вычислит все совпадения до того, как тело цикла будет введено).foreach затем загружает совпадения один за другим в $_ для вас, но вы никогда не используете переменную.pos в теле цикла бесполезен, так как содержит результат после окончания матчей.

Вывод здесь заключается в том, что если вы хотите, чтобы pos работал, и вы используете g модификатор, вы должны использовать цикл while, который налагает скалярный контекст и заставляет регулярное выражение перебирать совпадения в строке.

Синан вдохновил меня написать несколько foreach примеров:

  • Этот пример довольно лаконичен при использовании split в режиме удержания разделителя:

    my $pos = 0;
    foreach (split /($string)/i => $sequence) {
        print "Sequence found at position: $pos\n" if lc eq lc $string;
        $pos += length;
    }
    
  • Регулярный эквивалент решения split:

    my $pos = 0;
    foreach ($sequence =~ /(\Q$string\E|(?:(?!\Q$string\E).)+)/gi) {
        print "Sequence found at position: $pos\n" if lc eq lc $string;
        $pos += length;
    }
    
  • Но это, безусловно, лучшее решение для вашей проблемы:

    {package Dumb::Homework;
        sub TIEARRAY {
            bless {
                haystack => $_[1],
                needle   => $_[2],
                size     => 2**31-1,
                pos      => [],
            }
        }
        sub FETCH {
            my ($self, $index) = @_;
            my ($pos, $needle) = @$self{qw(pos needle)};
    
            return $$pos[$index] if $index < @$pos;
    
            while ($index + 1 >= @$pos) {
                unless ($$self{haystack} =~ /\Q$needle/gi) {
                    $$self{size} = @$pos;
                    last
                }
                push @$pos, pos ($$self{haystack}) - length $needle;
            }
            $$pos[$index]
        }
        sub FETCHSIZE {$_[0]{size}}
    }
    
    tie my @pos, 'Dumb::Homework' => $sequence, $string;
    
    print "Sequence found at position: $_\n" foreach @pos; # look how clean it is
    

    Причина, по которой это лучше всего, заключается в том, что два других решения должны сначала обработать все глобальное совпадение, прежде чем вы увидите результат.Для больших входов (например, ДНК) это может быть проблемой.Пакет Dumb::Homework реализует массив, который будет лениво находить следующую позицию каждый раз, когда итератор foreach запрашивает его.Он даже будет хранить позиции, чтобы вы могли добраться до них снова без повторной обработки.(По правде говоря, он выглядит на одно совпадение после запрошенного совпадения, это позволяет ему правильно завершиться в foreach, но все же намного лучше, чем обработка всего списка)Решение по-прежнему не использовать foreach, поскольку это не правильный инструмент для работы.

2 голосов
/ 01 февраля 2011

Объяснение Эрика о том, почему pos не будет работать должным образом в этом контексте, является точным замечанием.

Теперь, если насильно (я не знаю, с помощью каких полномочий ;-), использовать forцикл, вы можете использовать функцию index:

#!/usr/bin/env perl

use warnings; use strict;

my $sequence = <<EO_SEQ;
   AACAAATTGAAACAATAAACAGAAACAAAAATGGATGCGATCAAGAAAAAGATGC
   AGGCGATGAAAATCGAGAAGGATAACGCTCTCGATCGAGCCGATGCCGCGGAAGA
   AAAAGTACGTCAAATGACGGAAAAGTTGGAACGAATCGAGGAAGAACTACGTGAT
   ACCCAGAAAAAGATGATGCNAACTGAAAATGATTTAGATAAAGCACAGGAAGATT
   TATCTGTTGCAAATACCAACTTGGAAGATAAGGAAAAGAAAGTTCAAGAGGCGGA
   GGCTGAGGTAGCANCCCTGAATCGTCGTATGACACTTCTGGAAGAGGAATTGGAA
   CGAGCTGAGGAACGTTTGAAGATTGCAACGGATAAATTGGAAGAAGCAACACATA
   CAGCTGATGAATCTGAACGTGTTCGCNAGGTTATGGAAA
EO_SEQ

$sequence =~ s/\s//g;

my $string = <STDIN>;
chomp $string;

$string = uc $string;

for ( my $pos = 0; $pos >= 0; $pos = index($sequence, $string, $pos) ) {
    printf "Sequence found at position: %d\n", $pos;
    $pos += length $string;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...