Perlベストプラクティスを半分くらい読んだので、注意しようと思ったところをまとめます。
基本的なスタンス
コードが明確になることは、保守がしやすくなり発見しにくいバグや特異なケースが発生しにくくなる
1. PostFix型でなくBlock型の利用が推奨
my $hoge = &fuga(a,b,c) if ( $value > $MAX );
if ( $value > $MAX )
my $hoge = &fuga(a,b,c);
}
die("Error $!") if ( $value > $MAX );
- next, last, redo, return, goto, die, croak, throw は分かりやすく記載される為に、極力左側に配置する
2. 否定はあまり推奨されない
2.1. unless, until などを使うときは以下は使わない
!, not #値の否定
!~, !=, ne #値の不等
<,>,<=,>=,<=>, #数字の大きさ
lt,gt,le,ge,cmp, #文字列の大きさ
2.2. 例
next NAME unless $name ne 'Harry';
redo NAME unless $name !~ m/Mud{1,2}/xms;
next NAME unless $name eq 'Harry';
redo NAME unless $name =~ m/Mud{1,2}/xms;
3. パッケージ変数は使わない
- レキシカル変数(my で宣言するやつ)を使いましょう
4. else前後のカッコ
if () {
hoge
}
else {
fuga
}
5. => の呼び名
- fat comma (ファットコンマ)
6. $_ は非推奨
- 紛らわしいから。レキシカル変数で代用できるところは代用しましょう
7. 非レキシカルループ反復子
- forループの反復子変数は常にmyで宣言する
- forループで明示的な反復変数を使用する際は、必ずmyキーワードを使用して、レキシカル変数として明示的に宣言する
my $client;
SEARCH:
for $client (@clietns) {
last SEARCH if $client->holding();
}
if ($client) {
$client->resume_conversation();
}
myを省略するとループの手前で宣言されたレキシカル変数は再利用されなくなる。
代わりに、反復子変数として(同じく$clientという名前の)新しいレキシカル変数がこっそりと宣言される。
この新しいレキシカル変数のスコープは常にループブロックに限定され、外側のスコープで同じ名前を持つ変数を全て隠蔽する。
つまり、 forループ前の$clientとは別物 。
my $client;
SEARCH:
for $different_client_just_named_client (@clietns) {
last SEARCH if $different_client_just_named_client->holding();
}
if ($client) {
$client->resume_conversation();
}
つまりif文は実行されない
my $client_holding;
SEARCH:
for my $client (@clietns) {
if ($client->holding()){
$client_holding = $client;
last SEARCH;
}
}
if ($client_holding) {
$client_holding->resume_conversation();
}
8. リストの生成
8-1. 古いリストから新しいリストを作成するときはforでなくmapの利用
my @sqrt_result;
for my $result (@results) {
push @sqrt_result, sqrt($result);
}
@sqrt_result配列を設定する度に内部でメモリの再割当てが繰り返される。
配列の要素は予め割り当てられている為、pushを使用するとその後ろに新しい要素が配置されるので、
事前に領域を割り当てるとしたら、明示的なカウンタを使用しなくてはならない。
my @sqrt_result;
#$resultの要素集に応じて事前に領域を割り当てる
$#sqrt_result = $#result;
for my $next_sqrt_result (0..$#result) {
$sqrt_result[$next_sqrt_result] = sqrt $result[$next_sqrt_result];
}
代替案としてmapを使うといい
コード量も見栄えもシンプルになる。
内部でどのような処理が行われるか明示的になる。
my $sqrt_result = map { sqrt $_ } $result;
8-2. リストの値の検索にはforでなくgrep,firstを利用
- リストから不要な要素を削除したい場合
my @disqualified_candidates;
for my $name (@candidates) {
if (cannot_tell_a_lie($name)){
push @disqualified_candidates, $name;
}
grepを利用すると以下の様に済む。
my @disqualidied_candidates = grep { cannnot_tell_a_lie($_)} @candidates;
- 特定の要素を検索する場合
my $scapegoat = $disqualified_candidates[rand @disqualified_candidates];
SEARCH:
for my $name (@disqualified_candidates){
if ( chopped_down_cherry_tree )($name){
$scapegoat = $name;
last SEARCH;
}
print {$headline} "Disgraced $spacegoat Disqualified From Election\n";
firstを利用すると以下の様になる。
use List::Util qw( first );
my $spacegoat = first { chopped_down_cherry_tree{$_}} @disqualified_candidates;
if (!defined $scapegoat){
$scapegoat = $disqualified_candidates[rand @disqualified_candidates];
}
print {$headline} "Disgraced $scapegoat Disqualified From Election\n";
9. ドキュメント
ドキュメントはセックスに似ている。よいときはすごくよいし、よくなくてもないよりはマシだ。 Dick Brandon
ドキュメントは将来の自分に書くラブレターである、
- Pod (Plain Old Documentation) を利用する
=head1
SCRIPT NAMESomeScript.pl
=head1 DESCRIPTION
This script is used to do ....
=head1 USAGE
perl SomeScript.pl file1 file2 ...
=head1 SUBROUTINES/METHODS
メソッドについて
=head1 CONFIGURATION AND ENVIRONMENT
モジュールが使用するコンフィグシステムや環境変数などを説明
=head1 DEPENDENCIES
依存関係
バージョン情報、
必須モジュール
=head1 BUGS AND LIMITATIONS
問題や将来にリリース予定
パフォーマンス上の問題や未対応のデータ型など
=head1 INCOMPATIBLITIES
このモジュールと併用できないモジュール
=head1DIAGNOSTICS
エラーやメッセージ
発生しないはずのものも含む
問題毎に完全な説明、原因として考えられるもの、推奨される修復作
=cut
- こうしたドキュメントファイルはファイルごとに一箇所にまとめる必要がある
10. コンストラクタの呼び出しではアロー演算子を利用する
コンストラクタの呼び出しはアロー演算子を使用するのがよい。
Perlではコンストラクタと他のメソッドに実質的な違いはない。
my $obj = SomeClass->new;
間接オブジェクト呼び出しは、将来的には非推奨になる可能性がある。
my $obj = new SomeClass();