我必须使用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);
}
Run Code Online (Sandbox Code Playgroud)
这是我的foreach方法:
foreach ($sequence =~ /$string/gi )
printf "Sequence found at position: %d\n", pos($sequence) - length($string);
}
Run Code Online (Sandbox Code Playgroud)
有人可以告诉我为什么它不能以同样的方式工作吗?谢谢!
如果我输入"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
Run Code Online (Sandbox Code Playgroud)
Eri*_*rom 10
你的问题是上下文.在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;
}
Run Code Online (Sandbox Code Playgroud)正则表达式相当于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;
}
Run Code Online (Sandbox Code Playgroud)但这显然是解决您问题的最佳方案:
{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
Run Code Online (Sandbox Code Playgroud)
它最好的原因是因为其他两个解决方案必须首先处理整个全局匹配,然后才能看到结果.对于可能有问题的大输入(如DNA).该Dumb::Homework包实现了一个数组,每次foreach迭代器请求它时都会懒洋洋地找到下一个位置.它甚至会存储位置,以便您可以再次访问它们而无需重新处理.(事实上,它看起来超过了所请求的匹配,这使得它可以正常结束foreach,但仍然比处理整个列表要好得多)
实际上,最好的解决方案仍然是不使用,foreach因为它不是正确的工具.