することをお勧めしuse strict; use warnings
ます。これにより、変数を宣言する必要があり ( で行うことができますmy
)、多くの可能性のあるエラーを排除します。
ここに私が気づいたいくつかのことがあります:
say
Perl5 v10 以降では、関数 (use 5.010
または)を使用できますuse feature 'say'
。これは同様print
に機能しますが、最後に改行を追加します。
open の引数が 2 つの形式は使用しないでください。これにより、いくつかのセキュリティの問題が発生します。明示的なオープン モードを提供します。また、スカラーをファイルハンドルとして使用できます。これにより、ファイルの自動クローズなどの優れた機能が提供されます。
open my $INFILE, '<', $samplefile or die "Can't open $samplefile: $!";
変数には、失敗し$!
た理由が含まれています。open
配列から要素のリストを取得する場合は、スライス (複数の添え字) を使用できます。
my $res = join '', @columns[17 .. 19]; # also, range operator ".."
@
複数の要素を使用するため、シジルが になっていることに注意してください。
これsplice @columns, 0
は、「配列からすべての要素を削除し、それらを返す」という凝った言い方です。これは必須ではありません (後でその変数から読み取る必要はありません)。レキシカル変数 ( で宣言my
) を使用すると、ループの反復ごとwhile
に新しい変数を受け取ります。本当に内容を削除したい場合は、undef @columns
. これはより効率的なはずです。
実際のエラー:$flag = 0
ループを開始する前にステートメントを終了するには、後にセミコロンが必要です。
実際のエラー: C スタイルの for ループには、括弧で囲まれた 3 つの式が含まれています。最後のセミコロンはそれらを 4 つの式に分割します。これはエラーです。単純に削除するか、次のヒントをご覧ください。
C スタイルのループ ( for (foo; bar; baz) {}
) は面倒で、エラーが発生しやすくなります。範囲(インデックスなど)のみを反復する場合は、範囲演算子を使用できます。
for my $i (0 .. $#residue_name) { ... }
シジルは、配列の$#
最後のインデックスを示します。
配列に添え字を付ける (配列要素にアクセスする) 場合は、インデックスの印を含める必要があります。
$residue_name[$i]
$
1 つの要素のみにアクセスするため、配列の記号は であることに注意してください。
パターン$var = $var + 1
は に短縮できます$var++
。これはインクリメント演算子を使用します。
ゼロを除くすべての数値が真と見なされる$flag == 0
ため、 は と省略できます。!$flag
これはスクリプトの再実装です。ファイル名をコマンド ライン引数として受け取ります。これは、ユーザーにプロンプトを表示するよりも柔軟です。
#!/usr/bin/perl
use strict; use warnings; use 5.010;
my $filename = $ARGV[0]; # @ARGV holds the command line args
open my $fh, "<", $filename or die "Can't open $filename: $!";
my @residue_name;
my @residue_count;
while(<$fh>) { # read into "$_" special variable
next unless /^ATOM/; # start a new iteration if regex doesn't match
my $number = join "", (split //)[17 .. 19]; # who needs temp variables?
my $push_number = 1; # self-documenting variable names
for my $i (0 .. $#residue_name) {
if ($residue_name[$i] == $number) {
$residue_count[$i]++;
$push_number = 0;
}
}
push @residue_name, $number if $push_number;
# are you sure you want to print this after every input line?
# I'd rather put this outside the loop.
for my $i (0 .. $#residue_name) {
say $residue_name[$i], ("-" x 7), $residue_count[$i]; # "x" repetition operator
}
}
そして、大きな入力ファイルに対してより高速な実装を次に示します。配列をループする代わりに、ハッシュ (ルックアップ テーブル) を使用します。
#!/usr/bin/perl
use strict; use warnings; use 5.010;
my $filename = $ARGV[0]; # @ARGV holds the command line args
open my $fh, "<", $filename or die "Can't open $filename: $!";
my %count_residue; # this hash maps the numbers to counts
# automatically guarantees that every number has one count only
while(<$fh>) { # read into "$_" special variable
next unless /^ATOM/; # start a new iteration if regex doesn't match
my $number = join "", (split //)[17 .. 19]; # who needs temp variables?
if (exists $count_residue{$number}) {
# if we already have an entry for that number, we increment:
$count_residue{$number}++;
} else {
# We add the entry, and initialize to zero
$count_residue{$number} = 0;
}
# The above if/else initializes new numbers (seen once) to zero.
# If you want to count starting with one, replace the whole if/else by
# $count_residue{$number}++;
# print out all registered residues in numerically ascending order.
# If you want to sort them by their count, descending, then use
# sort { $count_residue{$b} <=> $count_residue{$a} } ...
for my $num (sort {$a <=> $b} keys %count_residue) {
say $num, ("-" x 7), $count_residue{$num};
}
}