Методы рекурсии Perl? - PullRequest
       18

Методы рекурсии Perl?

1 голос
/ 18 сентября 2009

Мне нужно немного помочь с этим кодом. Я знаю разделы, которые должны быть рекурсивными, или, по крайней мере, я думаю, что знаю, но не уверен, как это реализовать. Я пытаюсь реализовать программу поиска пути из матрицы выравнивания, которая найдет несколько маршрутов обратно к нулевому значению. Например, если вы извините мой код и вставили CGCA в качестве первой последовательности и CACGTAT в качестве второй последовательности, а 1, 0 и -1 - в качестве совпадений, несовпадений и разрывов. Программа выделяет путь как HDHHDD, а aligment как

CACGTAT

CGC - A -.

Однако есть и другие возможные пути и связи, кроме того, что я не знаю, сколько. То, что я хочу сделать, это вернуть часть моего цикла кода обратно на себя и найти другие пути и выравнивания, используя тот же код, что и в первый раз, пока не закончатся возможные выравнивания. Лучший способ, который я нашел в сети, - это рекурсия, за исключением того, что никто не может объяснить, как это сделать. В этом случае должно быть еще два пути и aligmennts HDDDHHD и CACGTAT, и C - GCA-, и. HDDDDHH, CACGTAT И --CGCA-. Я просто не знаю, как написать код для выполнения этой задачи.

# Implementation of Needleman and Wunsch Algorithm

my($seq1, $len1, $seq2, $len2, $data, @matrix, $i, $j, $x, $y, $val1, $val2);
my($val3, $pathrow, $pathcol, $seq1loc, $seq2loc, $gapscore, $matchscore, $mismatchscore);

#first obtain the data from the user. 
print "Please enter the first sequence for comaprsion\n";
$seq1=<STDIN>;
chomp $seq1;

print "Please enter the second sequence for comparsion\n";
$seq2=<STDIN>;
chomp $seq2;


# adding extra characters so sequences align with matrix
# saves some calculations later on
$seq1 = " " . $seq1;
$seq2 = " " . $seq2;
$len1 = length($seq1);
$len2 = length($seq2);

print "Enter the match score: ";
$matchscore=<STDIN>;
chomp $matchscore;

print "Enter the mismatch score: ";
$mismatchscore=<STDIN>;
chomp $mismatchscore;

print "Enter the gap score: ";
$gapscore=<STDIN>;
chomp $gapscore;

# declare a two dimensional array and initialize to spaces
# array must contain one extra row and one extra column
@matrix = ();  
for($i = 0; $i < $len1; $i++){  
   for($j = 0; $j < $len2; $j++){  
      $matrix[$i][$j] = ' ';  
   }  
}  

# initialize 1st row and 1st column of matrix  
$matrix[0][0] = 0;  
for ($i = 1; $i < $len1; $i ++){  
    $matrix[$i][0] = $matrix[$i-1][0] + $gapscore;  
}  
for ($i = 1; $i < $len2; $i ++){  
    $matrix[0][$i] = $matrix[0][$i-1] + $gapscore;  
}  


# STEP 1:
# Fill in rest of matrix using the following rules:
# determine three possible values for matrix[x][y]
# value 1 = add gap score to matrix[x][y-1]
# value 2 = add gap score to matrix[x-1][y]
# value 3 = add match score or mismatch score to 
#           matrix[x-1][y-1] depending on nucleotide 
#           match for position x of $seq1 and position y
#           of seq2
# place the largest of the three values in matrix[x][y]
#
# Best alignment score appears in matrix[$len1][$len2].

for($x = 1; $x < $len1; $x++){  
   for($y = 1; $y < $len2; $y++){  
 $val1 = $matrix[$x][$y-1] + $gapscore;  
 $val2 = $matrix[$x-1][$y] + $gapscore;  
 if (substr($seq1, $x, 1) eq substr($seq2, $y, 1)){  
           $val3 = $matrix[$x-1][$y-1] + $matchscore;  
 }  
 else{  
    $val3 = $matrix[$x-1][$y-1] + $mismatchscore;  
 }  
 if (($val1 >= $val2) && ($val1 >= $val3)){  
    $matrix[$x][$y] = $val1;  
 }  
 elsif (($val2 >= $val1) && ($val2 >= $val3)){  
    $matrix[$x][$y] = $val2;  
 }  
 else{  
    $matrix[$x][$y] = $val3;  
 }  
   }  
}  

# Display scoring matrix  
print "MATRIX:\n";   
for($x = 0; $x < $len1; $x++){  
   for($y = 0; $y < $len2; $y++){  
 print "$matrix[$x][$y] ";  
   }  
   print "\n";  
}  
print "\n";    

# STEP 2:  
# Begin at matrix[$len1][$len2] and find a path to   
# matrix[0][0].  
# Build string to hold path pattern by concatenating either   
# 'H' (current cell produced by cell horizontally to left),   
# 'D' (current cell produced by cell on diagonal),   
# 'V' (current cell produced by cell vertically above)
# ***This is were I need help I need this code to be recursive, so I can find more then one path***

$pathrow = $len1-1;  
$pathcol = $len2-1;  

while (($pathrow != 0) || ($pathcol != 0)){  
    if ($pathrow == 0){  
       # must be from cell to left  
       $path = $path . 'H';  
       $pathcol = $pathcol - 1;  
    }  
    elsif ($pathcol == 0){  
       # must be from cell above  
       $path = $path . 'V';  
       $pathrow = $pathrow - 1;  
    }  
    # could be from any direction  
    elsif  (($matrix[$pathrow][$pathcol-1] + $gapscore) == $matrix[$pathrow][$pathcol]){  
       # from left  
       $path = $path . 'H';  
       $pathcol = $pathcol - 1;  
    }  
    elsif (($matrix[$pathrow-1][$pathcol] + $gapscore) == $matrix[$pathrow][$pathcol]){  
       # from above  
       $path = $path . 'V';  
       $pathrow = $pathrow - 1;  
    }   
    else{  
       # must be from diagonal  
       $path = $path . 'D';  
       $pathrow = $pathrow - 1;  
       $pathcol = $pathcol - 1;  
    }  


}   



print "Path is $path\n";   

# STEP 3:
# Determine alignment pattern by reading path string 
# created in step 2.
# Create two string variables ($alignseq1 and $alignseq2) to hold 
# the sequences for alignment.
# Reading backwards from $seq1, $seq2 and path string, 
#   if string character is 'D', then 
#      concatenate to front of $alignseq1, last char in $seq1 
#      and to the front of $alignseq2, last char in $seq2
#   if string character is 'V', then 
#      concatenate to front of $alignseq1, last char in $seq1 
#      and to the front of $alignseq2, the gap char
#   if string character is 'H', then 
#      concatenate to front of $alignseq1 the gap char
#      and to the front of $alignseq2, last char in $seq2
# Continue process until path string has been traversed.
# Result appears in string $alignseq1 and $seq2
***#I need this code to be recursive as well.***

$seq1loc = $len1-1;  
$seq2loc = $len2-1;  
$pathloc = 0;  
print length($path);  
while ($pathloc < length($path)){  
   if (substr($path, $pathloc, 1) eq 'D'){  
      $alignseq1 = substr($seq1, $seq1loc, 1) . $alignseq1;  
      $alignseq2 = substr($seq2, $seq2loc, 1) . $alignseq2;  
      $seq1loc--;  
      $seq2loc--;  
   }  
   elsif (substr($path, $pathloc, 1) eq 'V'){  
      $alignseq1 = substr($seq1, $seq1loc, 1) . $alignseq1;  
      $alignseq2 = '-' . $alignseq2;  
      $seq1loc--;  
   }    
   else{  # must be an H  
      $alignseq1 = '-' . $alignseq1;  
      $alignseq2 = substr($seq2, $seq2loc, 1) . $alignseq2;  
      $seq2loc--;  
   }    
   $pathloc++;  
}  

print "\nAligned Sequences:\n";  
print "$alignseq2 \n";  
print "$alignseq1 \n";  

# statement may be needed to hold output screen  
print "Press any key to exit program";  
$x = <STDIN>;   

Если кому-то интересно, это алгоритм рукодела-вунша. Любая помощь здесь будет очень полезна.

Ответы [ 3 ]

7 голосов
/ 18 сентября 2009

Я не могу дать ответ, потому что я не совсем понимаю, что вы пытаетесь сделать, но я могу дать несколько общих советов.

Начните организовывать ваш код в отдельные подпрограммы, которые выполняют узко определенные задачи. Кроме того, подпрограммы, которые реализуют ваши центральные алгоритмы, не должны быть ориентированы на получение ввода с клавиатуры и выдачу вывода на экран; скорее они должны получать входные данные в качестве аргументов и возвращать свои результаты. Если требуется ввод данных пользователем или вывод на экран, эти задачи должны быть в отдельных подпрограммах, а не в ваших основных алгоритмах.

Первый (и частичный) шаг по этому пути - взять всю программу, заключить ее в определение подпрограммы и затем вызвать подпрограмму с необходимыми аргументами. Вместо того чтобы печатать свои ключевые результаты, подпрограмма должна возвращать их - в частности, ссылку на @matrix вместе со значениями для $path, $alignseq1, $alignseq2.

sub NW_algo {
    my ($seq1, $seq2, $matchscore, $mismatchscore, $gapscore) = @_;

    # The rest of your code here, but with all print
    # statements and <STDIN> inputs commented out.

    return \@matrix, $path, $alignseq1, $alignseq2;
}

my(@return_values) = NW_algo('CGCA', 'CACGTAT', 1, 0, -1);

Print_matrix($return_values[0]);

sub Print_matrix {
    for my $m ( @{$_[0]} ){
        print join(' ', @$m), "\n";
    }
}

На этом этапе у вас будет алгоритм, который может быть вызван другим кодом, чтобы упростить тестирование и отладку вашей программы в будущем. Например, вы можете определить различные наборы входных данных и запустить NW_algo() для каждого набора. Только тогда можно будет думать о рекурсии или других методах.

5 голосов
/ 18 сентября 2009

Поскольку Needleman-Wunsch - это алгоритм динамического программирования, большая часть работы уже выполнена к тому времени, когда вы вычисляете матрицу DP. Получив матрицу DP, вы должны вернуться через матрицу, чтобы найти оптимальное выравнивание. Проблема немного похожа на геометрию такси, за исключением того, что вы можете двигаться по диагонали. По сути, когда вам нужно вернуться назад по матрице, вместо того, чтобы выбирать между повышением, оставлением или диагональю, вы делаете все три, делая три рекурсивных вызова, и каждый из них вызывает себя для каждого вверх, влево или по диагонали Вы достигли своей отправной точки. Путь, пройденный каждой цепью рекурсии, будет вытягивать каждое выравнивание.

РЕДАКТИРОВАТЬ: Таким образом, в основном вам нужно поместить Шаг 2 в подпроцедуру (которая занимает позицию и пройденный путь), чтобы он мог вызывать себя снова и снова. Конечно, после того, как вы определили процедуру, вам нужно сделать один вызов, чтобы фактически запустить процесс трассировки:

sub tracePaths {
    $x = shift;
    $y = shift;
    $pathSoFar = shift; # assuming you're storing your path as a string
    #
    # ... do some work ...
    #
    tracePaths($x - 1, $y, $pathSoFar . something);
    tracePaths($x, $y - 1, $pathSoFar . somethingelse);
    tracePaths($x - 1, $y - 1, $pathSoFar . somethingelselse);
    #
    #
    #
    if(reached the end) return $pathSoFar;
}
# launch the recursion
tracePaths(beginningx, beginningy, "");
4 голосов
/ 18 сентября 2009

Это не относится конкретно к вашей проблеме, но вам, возможно, стоит почитать книгу Perl высшего порядка . В нем рассказывается, как использовать множество высокоуровневых техник (таких как рекурсия).

...