Effective Perl その2

Effective Perl (ASCII Addison Wesley Programming Series)

10項 区切り記号の使いすぎを避ける

&&とand、||とorは等価じゃないのか。リスト演算子、代入、結合を囲むかっこを省略できるっていうだけの違い?

14項 数限りなきソート術を学ぼう

Orcish Maneuver
名前がイカス。
普通にソートを書くとこんなんですが、

@sorted = sort { -M $a <=> -M $b} @files;

orの結果をcacheしていくと

@sorted = sort {
  ($cache{$a} ||= -M $a) <=>
  ($cache{$b} ||= -M $b)
} @files;

こんな風にかけるそうな。
でも、キャッシュ用のハッシュを使うから、宣言するか、

{
  my %cache;
  @sorted = sort {
    ($cache{$a} ||= -M $a) <=>
    ($cache{$b} ||= -M $b)
  } @files;
}

部分的にstrictを外さなければならない

no strict 'vars';
@sorted = sort {
  ($cache{$a} ||= -M $a) <=>
  ($cache{$b} ||= -M $b)
} @files;
use strict;

のがちょっといまいち。名前はステキなんだけど。

ということなんでもっとステキなのが、シュワルツ変換だそうで。

my @dest =
  map { $_->[0] }
  sort { $a->[1] <=> $b->[1] }
  map { [ $_, -M ] }
  @names;

0番目のとこに元リストの要素、1番目のとこにソートに使う情報をいれたリスト、を要素として持ったリストを作って、
1番目の要素を使ってソートをして、
その結果をもとに0番目の要素、つまり元リストの要素を返す、ということか。
うーん、余計な変数を作ったりしないで済むし、なんか速いらしい*1んでステキだ。

15項 正規表現演算子の優先順位を覚えよう
最高 () カッコとかのグループ演算子
?、*、{m,n} 繰り返し
foo シーケンス
最低 バーチカルバー*2 選択肢

わかりやすい。


んーーー、正規表現の細かいところは、僕にとっては、かなり気合を入れて取り組まないと理解できなそうなんでパスします。
使いこなせば本当に強力なんだろうけど、けっこうなリソースを費やさないといけなそうだから、それだったらほかのことをやります。

23項 myとlocalの違いを理解しよう
my $a = 1.7320508;
{
 my $a = 1.41421356;
 print $a. "\n";
}
print $a. "\n";

こんなんを実行すると、

1.414210356
1.7320508

こんなんが返ると。
変数のシャドーイングができるのね。
シャドーイングは嫌い。禁止しちゃえばいいのに。

24項 @_を直接使うのは(そうしなければならない場合を除いて)避けること

サブルーチンに直で渡されたリストをいじったところで、呼び出し元には影響しない。リファレンスだったら影響する。
うん。そうですね。

26項 コピーの代わりにリファレンスを渡そう

ファイルハンドルを使うときは、

IO::File*3
これからのトレンドだそうで。単純なオブジェクトとして、統一されたアプローチができるから楽っぽいかも。
ioref
んー。*FILE{IO}でも渡せるんだ。知らなかった。あんまり馴染んでないから使わないかな。
型グロブ
普通ですね。普通が一番。
29項 サブルーチンを使って、別のサブルーチンを作る

複数のクロージャで横断的に変数を保持することもできますよと。

my ( $foo, $bar ) = var_share_closure();

foreach ( 0 .. 5 ) {
 print $foo->() . ' ' . $bar->() . "\n";
}

sub var_share_closure {
 my $count = 0;
 return ( sub { $count++ }, sub { $count++ } );
}

風呂入りながらクロージャによる変数の保持のことを考えてたんですが、Perlでのインスタンス変数って、クラスに紐づいてるハッシュに入れて持ち歩くことで、インスタンス変数とみなすことをできるんじゃないかなーと思ったんですが、いじれないのか?

bless

で、脱線して、PerlのOOを考えてみるとblessってなんなんだろうということを理解してなかったです。
よくわかんないんで、とりあえずDumperしてみたんですが、

my $foo = bless {a => 'rr'}, "Foo";
print Dumper $foo;

そのまんまのが返ってくるし。

$VAR1 = bless( {a => 'rr'}, 'Foo' );

よくわかんないけど、そういうもんていうことかなー。
ここでblessの第一引数に渡してるハッシュっていじれないのかな。できないから、クロージャを使って云々ってことをしないといけないのか?
Programming Perl読むか。


で、id:naoyaさんのhttp://d.hatena.ne.jp/naoya/20060109/1136812096を見返してみて

 *{$declaredclass.'::'.$attribute} = $accessor;

の意味がやっとわかった。型グロブで展開させてソフトリファレンスとして扱うことで、シンボルテーブルに入れてるのね。はー、なるほど。すごい。そして型グロブって怖い。

そして、26項に戻って、
「クロージャと文字列evalによって、パターンマッチサブルーチンを作成する」ってことだけど、なんでevalなんだろう。普通のコード・リファレンスでいいんじゃないのと思ったけど、パターンマッチの問題なのね。
毎回コンパイルするっていうダサいのを避けるために、「一度だけコンパイルする」っていう意味の/oをつけてるけど、それだけだとプログラムを実行したときにだけコンパイルされちゃうのね。
なので、このパターンマッチサブルーチンを作ったときに毎回コンパイルさせるために、パターンマッチのところだけ別空間としてるのね。
なるほど。こりゃすごい。コメント書いとかないと、後で見直したときに理解するのに絶対時間がかかるな。

文字列evalの中で使っている/oの意味は「一度だけコンパイルする」に違いないが、ここでの「一度」は、プログラムを実行するたびに一度、ではなく、一回のevalにつき一度という意味になる。いささか「ずるい」手口だ。

  • 毎回コンパイル
sub make_match {
 my $pat = shift;
 return sub { grep /$pat/, @_ };
}

my $find = make_match('alpha');
print $find->('alphanumeric');

my $find2 = make_match('blavo');
# マッチするけど毎回コンパイルするのでオーバーヘッドがかかる
print $find2->('blavoblavo');
  • 一回だけコンパイル(バグってる版)
sub make_match {
 my $pat = shift;
 return sub { grep /$pat/o, @_ };
}

my $find = make_match('alpha');
print $find->('alphanumeric');

my $find2 = make_match('blavo');
# こっちはマッチしない
print $find2->('blavoblavo');
  • 一回だけコンパイル(きっちり動く版)
sub make_match {
 my $pat = shift;
 return eval 'sub { grep /$pat/o, @_ }';
}

my $find = make_match('alpha');
print $find->('alphanumeric');

my $find2 = make_match('blavo');
print $find2->('blavoblavo');


長くなったので、続きは別エントリ。

*1:Orcish Maneuverのほうがキャッシュするから速そうだけど

*2:バーチカルバーのエスケープの仕方がわからないや。できないの?

*3:ここもコロンのエスケープの仕方がわからない…