8

私は Perl を深く掘り下げ始めていますが、Perl で C を書く代わりに「Perl 風」のコードを書くのに苦労しています。次のコードを変更して、より多くの Perl イディオムを使用するにはどうすればよいですか? また、イディオムを学習するにはどうすればよいですか?

それが何をしているかの簡単な説明: このルーチンは、DNA またはアミノ酸配列を整列させるモジュールの一部です (そのようなことに関心がある場合は、Needelman-Wunch を使用してください)。2 つの 2 次元配列を作成します。1 つは 2 つのシーケンスの各位置のスコアを格納するためのもので、もう 1 つはパスを追跡するためのもので、後で最高スコアのアライメントを再作成できるようにします。それはうまくいきますが、私は物事を非常に簡潔かつ明確に行っていないことを知っています.

編集:これは割り当て用でした。完了しましたが、コードを少し整理したいと思います。アルゴリズムの実装に関する詳細は、興味のある方はクラスの Web サイトで見つけることができます。

sub create_matrix {
    my $self = shift;
    #empty array reference
    my $matrix = $self->{score_matrix};
    #empty array ref
    my $path_matrix = $self->{path_matrix};
    #$seq1 and $seq2 are strings set previously
    my $num_of_rows = length($self->{seq1}) + 1;
    my $num_of_columns = length($self->{seq2}) + 1;

    #create the 2d array of scores
    for (my $i = 0; $i < $num_of_rows; $i++) {
        push(@$matrix, []);
        push(@$path_matrix, []);
        $$matrix[$i][0] = $i * $self->{gap_cost};
        $$path_matrix[$i][0] = 1;
    }

    #fill out the first row
    for (my $i = 0; $i < $num_of_columns; $i++) {
        $$matrix[0][$i] = $i * $self->{gap_cost};
        $$path_matrix[0][$i] = -1;
    }
    #flag to signal end of traceback
    $$path_matrix[0][0] = 2;
    #double for loop to fill out each row
    for (my $row = 1; $row < $num_of_rows; $row++) {
        for (my $column = 1; $column < $num_of_columns; $column++) {
            my $seq1_gap = $$matrix[$row-1][$column] + $self->{gap_cost};
            my $seq2_gap = $$matrix[$row][$column-1] + $self->{gap_cost};
            my $match_mismatch = $$matrix[$row-1][$column-1] + $self->get_match_score(substr($self->{seq1}, $row-1, 1), substr($self->{seq2}, $column-1, 1));
            $$matrix[$row][$column] = max($seq1_gap, $seq2_gap, $match_mismatch);

            #set the path matrix
            #if it was a gap in seq1, -1, if was a (mis)match 0 if was a gap in seq2 1
            if ($$matrix[$row][$column] == $seq1_gap) {
                $$path_matrix[$row][$column] = -1;
            }
            elsif ($$matrix[$row][$column] == $match_mismatch) {
                $$path_matrix[$row][$column] = 0;
            }
            elsif ($$matrix[$row][$column] == $seq2_gap) {
                $$path_matrix[$row][$column] = 1;
            }
        }
    }
}
4

6 に答える 6

9

構文に関していくつかの提案が寄せられていますが、他にコードの読みやすさが理由ではない場合は、よりモジュール化されたアプローチもお勧めします。低レベルの詳細を気にする前に全体像を把握できれば、コードを理解するのははるかに簡単になります。

主な方法は次のようになります。

sub create_matrix {
    my $self = shift;
    $self->create_2d_array_of_scores;
    $self->fill_out_first_row;
    $self->fill_out_other_rows;
}

また、次のようないくつかの小さなメソッドもあります。

n_of_rows
n_of_cols
create_2d_array_of_scores
fill_out_first_row
fill_out_other_rows

さらに小さなメソッド (getter、setter など) を定義することで、それをさらに進めることができます。その時点で、中間レベルのメソッドのようなものcreate_2d_array_of_scoresは、基礎となるデータ構造に直接触れることはありません。

sub matrix      { shift->{score_matrix} }
sub gap_cost    { shift->{gap_cost}     }

sub set_matrix_value {
    my ($self, $r, $c, $val) = @_;
    $self->matrix->[$r][$c] = $val;
}

# Etc.
于 2009-10-23T17:27:00.020 に答える
8

簡単な変更の 1 つは、for次のようなループを使用することです。

for my $i (0 .. $num_of_rows){
    # Do stuff.
}

詳細については、foreach ループ範囲演算子に関する Perl のドキュメントを参照してください。

于 2009-10-23T16:32:27.620 に答える
7

次のように 2 次元配列を逆参照する代わりに、次のようにします。

$$path_matrix[0][0] = 2;

これを行う:

$path_matrix->[0][0] = 2;

また、特定のサブシーケンスと照合するために多くの if/then/else ステートメントを実行しています: これはgivenステートメントとしてより適切に記述できます (perl5.10 の C に相当するものswitch)。perldoc perlsynでそれについて読んでください:

given ($matrix->[$row][$column])
{
    when ($seq1_gap)       { $path_matrix->[$row][$column] = -1; }
    when ($match_mismatch) { $path_matrix->[$row][$column] = 0; }
    when ($seq2_gap)       { $path_matrix->[$row][$column] = 1; }
}
于 2009-10-23T16:36:25.147 に答える
7

他にもいくつかコメントがありますが、最初の意見は次のとおりです。

my $num_of_rows = length($self->{seq1}) + 1;
my $num_of_columns = length($self->{seq2}) + 1;

$self->{seq1}とは文字列$self->{seq2}であり、 を使用して個々の要素にアクセスし続けますsubstr。それらを文字の配列として保存することをお勧めします。

$self->{seq1} = [ split //, $seq1 ];

これが私がそれを書いた方法です:

sub create_matrix {
    my $self = shift;

    my $matrix      = $self->{score_matrix};
    my $path_matrix = $self->{path_matrix};

    my $rows = @{ $self->{seq1} };
    my $cols = @{ $self->{seq2} };

    for my $row (0 .. $rows) {
        $matrix->[$row]->[0] =  $row * $self->{gap_cost};
        $path_matrix->[$row]->[0] = 1;
    }

    my $gap_cost = $self->{gap_cost};

    $matrix->[0] = [ map { $_ * $gap_cost } 0 .. $cols ];
    $path_matrix->[0] = [ (-1) x ($cols + 1) ];

    $path_matrix->[0]->[0] = 2;

    for my $row (1 .. $rows) {
        for my $col (1 .. $cols) {
            my $gap1 = $matrix->[$row - 1]->[$col] + $gap_cost;
            my $gap2 = $matrix->[$row]->[$col - 1] + $gap_cost;
            my $match_mismatch =
                $matrix->[$row - 1]->[$col - 1] +
                $self->get_match_score(
                    $self->{seq1}->[$row - 1],
                    $self->{seq2}->[$col - 1]
                );

            my $max = $matrix->[$row]->[$col] =
                max($gap1, $gap2, $match_mismatch);

            $path_matrix->[$row]->[$col] = $max == $gap1
                    ? -1
                    : $max == $gap2
                    ? 1
                    : 0;
            }
        }
    }
于 2009-10-23T16:33:11.677 に答える
5

コードの大部分は 2D 配列を操作しています。配列で多くのことをしたい場合、特に効率が懸念される場合は、PDLの使用に切り替えることが最大の改善になると思います。これは優れた配列サポートを提供する Perl モジュールです。基礎となるルーチンは効率のために C で実装されているため、高速でもあります。

于 2009-10-23T16:34:16.573 に答える