Perlベストプラクティス(6章)

  • 自分が気になったとこだけを取り上げているので、実際はもっといっぱいベストプラクティスが載っていますよ!

第6章 制御構造

  • ここではif文やforなどのループを書く際の注意点について書かれています。
ポストフィックス形式のifはnext, last, redo, return, goto, die, croak, throw などによる制御フロー文のみに使用する。
  • 制御フローであることを目立たせるためです。
for my $i (@list) {
    last if $i < 0;
}
  • ポストフィックス形式関連で言うと、unless, for, while, untilは使わないということもありました。また否定のunless, until はどんな形式であれ使わないというのもありました。
  • unlessは例外処理などでつい使ってしまいますね。untilは使ったことないですが。
croak "throw exception!" unless $i;
  • 確かにandやorでの複数条件でのunlessはif文に比べるとわかりにくいとは思います。unlessでの!による否定の条件も。そういったわかりにくい制御構造が使われる恐れがあるので、それならいっそ禁止しようということのようです。
Cスタイルのループを使わない。
  • インデックスが必要な場合でも下記のように書けるしわかりやすいです。
# for (my $i=0; $i < $length; $i++) {
for my $i (0..$length) {
古いリストから新しいリストを生成する際は、forではなくmapを使用する
  • リストに対する処理として、forではなくmapやgrepやList::Utilに含まれているfirstなどの関数を使ったほうがいい場面について書かれているところはとても勉強になりました。
リストの値を検索する際には、forではなgrepとfirstを使用する。
  • 特定の要素が含まれている場合や特定の値が含まれているかどうか調べる場合にはfirstを使用するというのは使っていなかったので勉強になりました。
その場でリストを変換する際はmapではなくforを使用する。
  • そうすることでメモリを再利用出来るとあったので、パフォーマンス的にはどうなのかと思ってベンチマークとってみました。
#!perl

use strict;
use warnings;
use Benchmark qw/:all/;

my @list = (); 
for my $i (1..1000) {
    push @list, 'map and for'; 
}
my @list2 = @list;

cmpthese(3000, {
    map => sub {
        # 大文字に変換
        @list = map { uc } @list;
    },  
    for => sub {
        for my $i (@list2) {
            $i = uc $i; 
        }   
    },  
});

# Rate  map  for
# map 1031/s   -- -85%
# for 6818/s 561%   --
  • forの方が早いですね。
リスト関数で$_を変更しない
  • grepやmapやfirstなどで参照する$_はリスト要素の「エイリアス」、つまりそれを変更するともとのリストの値も変更されてしまい、バグのもとになりやすいのでやめましょうということですね。
  • 値を変換する場合は、$_を別の変数にコピーして処理することが推奨されています。
等価評価の連鎖ではなく、テーブル参照を使用する
  • 例えばidから都道府県の名前をとって来るときに、if〜elsif〜elseを47個繰り返すのではなく、ハッシュテーブルを作ってそれを参照するようにすることですね。
my $hash => {
    1 => '北海道',
    2 => '青森',
    :
};
:
my $name = $hash->{$id};
  • そんな47個もif〜elsif〜elseを重ねることをする人なんていないと思うかもしれませんが、実際見たことあります。。
  • 上記のような使い方の他に処理を分けたい場合は、ハッシュの値に関数を入れると出来ます。これを使うとシンプルに書けることって結構あるんじゃないかなと思います。
my $weather = {
   fine    => sub { print ' ( ^ ^ )' },
   cloud => sub { print ' ( - - ) ' },
   rain    => sub { print ' ( > < ) ' },
};
$weather->{$today}->();
値を生成する際には、表形式の3項演算子を使用する
  • 上記のようなハッシュテーブルでは表現が難しい条件の場合に、3項演算子を使用する方法もあります。
my $res = $url =~ /\.(pm|pl)$/       ? 'perl',
                : $url =~ /\.php$/   ? 'php',
                : $url =~ /\.py$/     ? 'python',
  • ひどいサンプル&インデントが上手く調整出来なかった。。
do-whileループは使用しない
  • 確かに業務では使ったことないかも。
線形コーディング
  • ループでのチェックをまとめて1箇所でするのではなく、それぞれの条件が整った段階で順次行っていくようにします。
  • こうすることで、読みやすさや効率化につながるとされています。
for my $v (@list) {
    next if !defined $v;
    :
    next if $n ne $v;
    :
}
ループの制御の一元化にこだわらない
  • ループはかならず1箇所にすべきという考えに固執するあまりにフラグをたくさん持つようにするのではなく、nextやlastやredoなどでループの制御を行う方が読みやすくなるというものです。これはまさにその通りだなと思います。
明示的に終了する全てのループにラベルをつける
  • これはどうなのでしょうか?本だと入れ子になっているなどは関係なしに付けるようにかかれています。
  • 自分の場合は、ループがネストしたりしている場合だけ付けるようにします。
  • いつも付ける理由としては、後にそのループの中に新たにループが追加されたときにでも最初から付けてあれば問題なく動作すること、ラベルがコメントの役割を果たすことで間違いが発見しやすいことが挙げられていました。
感想
  • 1〜5章からの流れでこの章も大変面白い章でした。制御構造の辺りは非効率的な実装をしてしまったりバグを埋め込んだりしやすい箇所だと思うのでしっかりベストプラクティスを身につけておきたいと思いました。
  • そして1〜5章の感想をもう細かく取り上げて書けばよかったと後悔しています。
  • 次の第7章は「ドキュメント」です!