Perlベストプラクティス - その1

Perlベストプラクティス

Perlベストプラクティス

を読んで初めて知ったこと、役に立つと思ったことをまとめてみる。

第5章:変数

5.8 $_の変更に注意

$_は言うに及ばずだが、myにも注意。実はエイリアス

my @list = (1..5);
for my $i (@list) {
    $i = 1;
}   
printf("%s\n", join q{,}, @list);
#1,1,1,1,1

第6章:制御構造

リストの生成

forではなくmapを使う

  • 可読性が高くなる
# OK
@double_vals = map { $_ * 2 } (1..5);

# NG
@double_vals = ();
for my $val (1..5) {
    push @double_vals, $val * 2;
}
リストの検索

forではなく、grepやfirstを使う

  • コードの可読性が高まる
  • 実行速度が速い
use Benchmark;
my @list = (1..20);

# NG
sub impl_by_for {
    my @multiple_of_3;
    for my $i (@list) {
        push @multiple_of_3, $i if ($i % 3 ==0);
    }
    return @multiple_of_3;
}

# OK
sub impl_by_grep {
    return grep { $_ % 3 == 0 } @list;
}

timethese(100_000, +{
    'impl_by_grep' => 'impl_by_grep()',
    'impl_by_for'  => 'impl_by_for()',
});
Benchmark: timing 100000 iterations of impl_by_for, impl_by_grep...impl_by_for:  3 wallclock secs ( 2.16 usr +  0.01 sys =  2.17 CPU) @ 46082.95/s (n=100000)impl_by_grep:  2 wallclock secs ( 1.55 usr +  0.01 sys =  1.56 CPU) @ 64102.56/s (n=100000)

どちらがよいか、一目瞭然。
細かいところちゃんと意識してやっていきたいなぁ。

リスト変換

mapではなく、forを使う

  • mapは一時的に配列のためのメモリを新たに割当てる
  • 既存の配列が大きいとその分メモリを食うので、変換であればforが望ましい

第8章:組み込み関数

reverse
  • リストコンテキストではリストを反転
print reverse (0..9), "\n";
print scalar(reverse 'abcdefg'), "\n";
テキストデータの分割
  1. 固定幅のフィールドの取得にはunpackを使う
  2. 可変幅で、シンプルなフィールドの取得にはsplitを使う
  3. 可変幅で、複雑なフィールドの取得にはText::CSV_XS(Text::CSV::Simple)を使う

1, 2は普通の話。実際にText::CSV_XSを使ってみる。データはidと名前だが、ダブルクォートがあったり、カンマの後にスペースがあったりとちょっと複雑。

11111,"kotaroito"
22222,kotaroito
33333,  kotaroito

スクリプトはこちら。

use Text::CSV::Simple;

my $csv_format = Text::CSV::Simple->new(+{
        sep_char         => q{,},
        quote_char       => q{"},
        allow_whitespace => 1,});
$csv_format->field_map( qw( id name ) );

my @names;
open my $fh, '<', 'test.csv';
for my $row_ref ($csv_format->read_file($fh)) {
        push @names, $row_ref->{name}; 
}
close $fh;
printf("%s\n", join qq{\n}, @names);
kotaro@mac:~$ perl test.pl
kotaroito
kotaroito
kotaroito

sep_charでフィールドを分割する文字を変えられるので、csvだけではなくtsvも可能。