如何在perl中使用数组匹配两个序列

时间:2013-05-02 19:35:59

标签: perl

当循环遍历两个数组时,我对如何将指针移动到一个循环但在另一个循环中保持不变感到困惑。例如:

  • 数组1:A T C G T C G A G C G
  • 数组2:A C G T C C T G T C G

因此,第一个数组中的A与第二个数组中的A匹配,因此我们继续使用下一个元素。但由于T与第二个索引中的C不匹配,我希望程序将该T与阵列2中的下一个G进行比较,依此类推,直到找到匹配的T.

my ($array1ref, $array2ref) = @_;

my @array1 = @$array1ref;
my @array2= @$array2ref;
my $count = 0; 
foreach my $element (@array1) {
 foreach my $element2 (@array2) {
 if ($element eq $element2) {
 $count++;
  }else { ???????????


}

7 个答案:

答案 0 :(得分:3)

您可以使用while循环来搜索匹配项。如果找到匹配项,请在两个阵列中前进。如果不这样做,请前进第二个阵列。最后,您可以打印第一个数组中剩余的不匹配字符:

# [1, 2, 3] is a reference to an anonymous array (1, 2, 3)
# qw(1, 2, 3) is shorthand quoted-word for ('1', '2', '3')
my $arr1 = [qw(A T C G T C G A G C G)];
my $arr2 = [qw(A C G T C C T G T C G)];

my $idx1 = 0;
my $idx2 = 0;

# Find matched characters
# @$arr_ref is the size of the array referenced by $arr_ref
while ($idx1 < @$arr1 && $idx2 < @$arr2) {
    my $char1 = $arr1->[$idx1];
    my $char2 = $arr2->[$idx2];
    if ($char1 eq $char2) {
        # Matched character, advance arr1 and arr2
        printf("%s %s  -- arr1[%d] matches arr2[%d]\n", $char1, $char2, $idx1, $idx2);
        ++$idx1;
        ++$idx2;
    } else {
        # Unmatched character, advance arr2
        printf(". %s  -- skipping arr2[%d]\n", $char2, $idx2);
        ++$idx2;
    }
}

# Remaining unmatched characters
while ($idx1 < @$arr1) {
    my $char1 = $arr1->[$idx1];
    printf("%s .  -- arr1[%d] is beyond the end of arr2\n", $char1, $idx1);
    $idx1++;
}

脚本打印:

A A  -- arr1[0] matches arr2[0]
. C  -- skipping arr2[1]
. G  -- skipping arr2[2]
T T  -- arr1[1] matches arr2[3]
C C  -- arr1[2] matches arr2[4]
. C  -- skipping arr2[5]
. T  -- skipping arr2[6]
G G  -- arr1[3] matches arr2[7]
T T  -- arr1[4] matches arr2[8]
C C  -- arr1[5] matches arr2[9]
G G  -- arr1[6] matches arr2[10]
A .  -- arr1[7] is beyond the end of arr2
G .  -- arr1[8] is beyond the end of arr2
C .  -- arr1[9] is beyond the end of arr2
G .  -- arr1[10] is beyond the end of arr2

答案 1 :(得分:2)

嵌套循环毫无意义。你不想多次循环任何东西。

您没有指定重新同步后您想要发生的事情,因此您需要从以下内容开始,并根据您的需要进行定制。

my ($array1, $array2) = @_;

my $idx1 = 0;
my $idx2 = 0;
while ($idx1 < @$array1 && $idx2 < @$array2) {
   if ($array1->[$idx1] eq $array2->[$idx2]) {
      ++$idx1;
      ++$idx2;
   } else {
      ++$idx2;
   }
}

...

原样,上面的代码段会将$idx1留在最后一个无法(最终)重新同步的索引处。如果您希望在第一次重新同步时立即停止,则需要

my ($array1, $array2) = @_;

my $idx1 = 0;
my $idx2 = 0;
my $mismatch = 0;
while ($idx1 < @$array1 && $idx2 < @$array2) {
   if ($array1->[$idx1] eq $array2->[$idx2]) {
      last if $mismatched;          
      ++$idx1;
      ++$idx2;
   } else {
      ++$mismatched;
      ++$idx2;
   }
}

...

答案 2 :(得分:0)

foreach循环不会削减它:我们要么在两个数组中都有可用元素时循环,要么遍历所有索引,我们可以随意增加:

EL1: while (defined(my $el1 = shift @array1) and @array2) {
  EL2: while(defined(my $el2 = shift @array2)) {
    ++$count and next EL1 if $el1 eq $el2; # break out of inner loop
  }
}

my $j = 0; # index of @array2
for (my $i = 0; $i <= $#array1; $i++) {
  $j++ until $j > $#array or $array1[$i] eq $array2[$j];
  last if $j > $#array;
  $count++;
}

或任何组合。

答案 3 :(得分:0)

这是复杂的循环使用环境的条件而不是

my ($array1ref, $array2ref) = @_;

my @array1 = @$array1ref;
my @array2= @$array2ref;
my $count = 0;
my ($index, $index2) = (0,0);
#loop while indexs are in arrays
while($index <= @#array1 && $index2 <= @#array2) { 
    if($array1[$index] eq $array2[$index2]) {
        $index++;
        $index2++;
    } else {
        #increment index until we find a match
        $index2++ until $array1[$index] eq $array2[$index2];
    }
}

答案 4 :(得分:0)

这是一种可能性。它将使用索引遍历两个列表。

my @array1 = qw(A T C G T C G A G C G);
my @array2 = qw(A C G T C C T G T C G);

my $count = 0;
my $idx1 = 0;
my $idx2 = 0;

while(($idx1 < scalar @array1) && ($idx2 < scalar @array2)) {
    if($array1[$idx1] eq $array2[$idx2]) {
        print "Match of $array1[$idx1] array1 \@ $idx1 and array2 \@ $idx2\n";
        $idx1++;
        $idx2++;
        $count++;
    } else {
        $idx2++;
    }
}

print "Count = $count\n";

答案 5 :(得分:0)

如果你保证array2总是和array1一样长或者更长,看起来你可以用'grep'很容易地做到这一点。像这样:

sub align
{
    my ($array1, $array2) = @_;
    my $index = 0;

    return grep
           {
               $array1->[$index] eq $array2->[$_] ? ++$index : 0
           } 0 .. scalar( @$array2 ) - 1;
}

基本上,grep说“将我的增加索引列表返回到array2中,与array1中的连续元素匹配。”

如果使用此测试代码运行上述代码,则可以看到它返回预期的对齐数组:

my @array1 = qw(A T C G T C G A G C G);
my @array2 = qw(A C G T C C T G T C G);

say join ",", align \@array1, \@array2;

这会输出预期的映射: 0,3,4,7,8,9,10。该列表表示@array1[0 .. 6]对应@array2[0,3,4,7,8,9,10]

(注意:您需要use Modern::Perl或类似使用say。)

现在,你还没有真正说过你需要输出的操作。我假设你想要这个映射数组。如果您只需要计算@array2中与@array1对齐时跳过的元素数量,您仍然可以使用上面的grep,而不是列表,只需{{1}最后。

答案 6 :(得分:0)

如您所知,您的问题称为Sequence Alignment。有很好的算法可以有效地完成这项工作,CPAN上有一个这样的模块Algorithm :: NeedlemanWunsch。以下是您如何将其应用于您的问题。

#!/usr/bin/perl

use Algorithm::NeedlemanWunsch;

my $arr1 = [qw(A T C G T C G A G C G)];
my $arr2 = [qw(A C G T C C T G T C G)];

my $matcher = Algorithm::NeedlemanWunsch->new(sub {@_==0 ? -1 : $_[0] eq $_[1] ? 1 : -2});

my (@align1, @align2);
my $result = $matcher->align($arr1, $arr2,
  {
   align   => sub {unshift @align1, $arr1->[shift]; unshift @align2, $arr2->[shift]},
   shift_a => sub {unshift @align1, $arr1->[shift]; unshift @align2,            '.'},
   shift_b => sub {unshift @align1,            '.'; unshift @align2, $arr1->[shift]},
  });

print join("", @align1), "\n";
print join("", @align2), "\n";

根据我们在构造函数中指定的成本打印出最佳解决方案:

ATCGT.C.GAGCG
A.CGTTCGG.TCG

与原始问题中的方法截然不同,但我认为值得了解。