2010年9月1日水曜日

Perl best practices[Perlベストプラクティス] 4章 値と式


このエントリーをはてなブックマークに追加


文字列の変数展開 文字列デリミタ4つのルール



  1. 変数を展開する場合は、2重引用符。

  2. 展開しない場合は、単一引用符。

  3. 単一引用符を文字列として扱いたい場合は、q{}を使用する。

  4. {を文字列として扱いたい場合は、q[]とする。



関連する文字列が並んでいる場合は、読み難いので、一つのルールで統一する。



my $title = 'Perl Best Practices';
my $publisher = q{O'Reilly};
my $end_of_block = '}';
my $closing_delim = q['}];
my $citation = "$title ($publisher)";

#以下に修正
my $title = q[Perl Best Practices];
my $publisher = q[O'Reilly];
my $end_of_block = q[}];
my $closing_delim = q['}];
my $citation = qq[$title ($publisher)];



空の文字列に"",''を使用しない


以下を使用のこと。



$error_msg = q{};



1文字の文字列


スペースやタブは



$a = q{ };
$b = "\t";


とすること。
リテラルの引用符も美しくないので、q{}を推奨。


名前付きエスケープを使用する。


Perlが表現できない文字は、数値のエスケープ(バックスラッシュとそれに続くASCII値を””で囲んだもの)で表せるが、use charnamesプラグマを用いて、名前付きエスケープを使用すること。



use charnames qw( :full);

$escape_seq = "\N{DELETE}\N{ACKNOWLEDGE}\N{CANCEL}Z"; # "\177\006\030z"



マジックナンバーはReadonlyで定数化する(use constantを使用しない)


定数はすべて大文字にし、定数名と値は=>(ファットコンマ)で見やすくする。



use Readonly;
Readonly my $BOOK_NUMBER => 42;


空文字q{ }を定数化すると便利。


なぜconstantを使用しないのか

use constantで作成されるのは裸のワードなので、定数が展開されない。
以下はReadonlyを使用した場合。



$msg = <<"END_MSG";
$BOOK_NUMBER
END_MSG

$title{$BOOK_NUMBER}




もっとも重要なことは、
レキシカルスコープをもつ定数を実行時に生成できること。



EVENT:
while (1) {
use Readonly;
Readonly my $EVENT => get_next_event();

last $EVENT if not defined $EVENT;

print "$EVENT\n";
}



8進数は組み込み関数octを使用する。


0で始まる整数で8進数を表現しない。


長い数字は桁区切りする


Perl5.8以降では、数字の間にアンダーバーを挿入することができる。(前versionは3桁区切りのみ)



$US_GDP = 10_990_000_000_000;



複数行の文字列は、\nで明示的に改行する



$usage = "Usage: aaaaa\n"
."bbbbb\n"
;


この\nを書かないで、



$usage = "Usage: aaaaa
bbbbb";


と文字列を折り返しても暗黙的に改行されるが、読みにくい。さらにインデントが左端による。


文字列が3行以上ならヒアドキュメントを使用する。



$Usage =<<"END_USAGE";
すきな文字列記述
END_USAGE



ヒアドキュメントのインデント問題


ヒアドキュメントはインデントが左端になってしまう。
定義済み変数かサブルーチン(ゼアドキュメント)にファクタリングする。



use Readonly
Readonly my $USAGE => <<'END_USAGE';
好きな文字列
END_USAGE



実行時に値を決定したい場合はサブルーチンを使う。



sub build_usage{
my($prog_name, $file_name) = @_;

return <<"END_USAGE";

$prog_name $file_name

END_USAGE
}



ヒアドキュメントのターミネータ


ラベルを見つけやすくするように、頭にENDを付ける。


ヒアドキュメントの引用符



  • <<のあとのラベルを単一引用符で囲むと、変数は展開されない

  • 2重引用符で囲むと変数が展開される

  • 引用符を使わない裸のラベルでは、2重引用符と同じ結果が得られるが、使用しないこと



裸のワードを使用しない。


use strict qw( subs )で使用を禁止する。
裸のワードは引数なしのサブルーチンと名前が被る。


ファットコンマ



  • ハッシュを生成する際に、=>を使用すれば、キーを引用符で囲む必要がない。

  • =>の外観は、値の移動や変化の印象を与え易いが、動作の解釈に誤解を与えることもあり、ハッシュエントリや名前付き引数、その他の名前と値の組、のときのみ使用したほうが混乱しない。



文の順序付けはコンマではなく、doブロックを使う



  • スカラーコンテキストでは、コンマは順序付け

  • リストコンテキストでは、コンマはセパレータ

  • 役割が2つあるのは混乱するので、順序付けにはdoブロックを使う。



C言語のforループのように、



for ($min=0, $max=$#samples, $found_target=0; $min<=$max) {
#略
}


と書いた場合、C言語と同じく、コンマは弱いセミコロンのような働きをし、順序付けを行う。
これはdoブロックで以下のようになる。



for (do{$min=0; $max=$#samples; $found_target=0;}; $min<=$max) {
#略
}



この場合は、そもそも初期値を指定しているdoブロックの中身をforの前に書いておく方がもっと良い。


優先度の高いブールと低いブールを混在させない



  • 優先度の低いand,notはいっさい使用しない。

  • orだけは、組み込み関数の代案を指定するために使用する。(or croak "...";など)




next CLIENT if not $finished || $result < $MIN_ACCEPTABLE; #これは以下のどれになるか

next CLIENT if (not $finished) || ($result < $MIN_ACCEPTABLE);#期待しているのはコレ

next CLIENT if not ($finished || $result < $MIN_ACCEPTABLE);#実際はコレ



リストは括弧で囲む


コンマは代入より優先順位が低いので、リストは常に括弧で囲む。


リストのメンバの評価には、any()を使用し、文字列リストの場合は、テーブル参照を利用


List::MoreUtilsモジュールのany()関数はgrepとほぼ同じだが、
値のどれかが評価ブロックをパスした時点で、trueの値を返して終了する。このため、grepよりも効率的。(パスしない場合は、いずれも値もfalseを返す。)
(でもfirstとanyは何が違う?)

これは、コードブロックに任意の等価評価を作成できるため。
if( any { $fugitive->also_known_as{$_} ) @guests ) {
#略
}


リストのメンバ評価でeqを使用する場合は、参照テーブルを使用するほうがはるかに効果的。


ハッシュアクセスは、配列を線形アクセスするよりも高速である(線形探索を短絡できる場合も含めて)し、コードも読み易い。



Readonly my %IS_EXIT_WORD
= map { $_ => 1 } qw(
q quit bye exit stop done last finish aurevoir
);

if( $IS_EXIT_WORD{$cmd} ) {
abort_run();
}





0 件のコメント:

コメントを投稿