燈明ブログ

現状は小池啓仁の応援ブログ

「なぜ split $;, $_[1] なのかは宿題」の答え

まずは、問題。

1から100までの数をプリントするプログラムを書け。
ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。

http://www.aoky.net/articles/jeff_atwood/why_cant_programmers_program.htm

弾さんの答え

tie my %fizzbuzz, 'Tie::Code' => sub {
    $_[0] % 15 ? $_[0] % 5 ? $_[0] % 3 ? 
    $_[0]      : 'Fizz'    : 'Buzz'    : 'FizzBuzz';
};

print "$fizzbuzz{$_}\n" for (1..100);

答えは感嘆せざるをえないほど簡単だ。

package Tie::Code;
use Tie::Hash;
sub TIEHASH { bless $_[1], $_[0] }
sub FETCH   { $_[0]->( split $;, $_[1] ) }

なぜsplit $;, $_[1]なのかは宿題ということで。

http://blog.livedoor.jp/dankogai/archives/50826956.html


そして、宿題の答えなのですが、わかりませんでした!
わかったのは、「split $;」しても、しなくても結果は同じになったということです。


$_[1]には1から100のスカラー値が設定されます。
そして「split $; $_[1]」すると配列値(ゼロ番目にそのスカラー値)になります。
しかし、これを受ける方(無名サブルーチン)で、第一引数の$_[0]になっていますので、スカラー値でも配列値も同じ値になりますよね。
実際、実行結果でも同じでした。


ちなみに、私的にソース解説をしときました。

tie my %fizzbuzz, 'Tie::Code' => sub {
    $_[0] % 15 ? $_[0] % 5 ? $_[0] % 3 ? 
    $_[0]      : 'Fizz'    : 'Buzz'    : 'FizzBuzz';
};

は、入れ子の3項演算子使用で、以下のようにすると意味が理解しやすくなります。

tie my %fizzbuzz, 'Tie::Code' => sub {
    $_[0] % 15 ? $_[0] % 5 ? $_[0] % 3 ? $_[0]
                                       : 'Fuzz'
                           : 'Bumper'
               : 'FuzzBumper';
};
  • 15で割れなくて、5で割れなくて、3で割れないと$_[0]をリターン値とする。
  • 15で割れなくて、5で割れなくて、3で割れると'Fuzz'をリターン値とする。
  • 15で割れなくて、5で割れると'Bumper'をリターン値とする。
  • 15で割れると'FuzzBumper'をリターン値とする。

尚、「・・・ 'Tie::Code' => sub{ ・・・」等でハッシュぽっく書いてあるけど、この無名サブルーチンリファレンスは、コンストラクタ(Tie::CodeのTIEHASH)の第2引数にセットされる。

print "$fizzbuzz{$_}\n" for (1..100);

は、以下と同じです。

for $_ (1..100) {
    print "$fizzbuzz{$_}\n";
}