第32回 前回のソースの説明
さて、どこから説明したらいいものやら......
という感じなのですが、前回のソースの説明をしたいと思います。
前回のソースはもうひとつ窓を開けてもらって(別のブラウザとかで見てもらって)ここでは、部分的に説明していきたいと思います。
まずは、
new
33 my $human = Human::new({'name' => 'ビンゴ', 34 'age' => 1, 35 'bag' => {} 36 });
このnew をしている部分からいきたいと思います。
new関数自体は次のようになっています。
5 sub new { 6 my $c = shift; 7 bless $c; 8 }
単に、受け取ったものをblessしているだけ。
つまり、受け取ったものに魔法をかけて、Humanパッケージの関数を使えるようにしているわけですね。
おっと!
そうそう、主人公をつくるためのパッケージは、Humanという名前にしました。
このHumanには主人公にできることがたくさん関数として書かれています。
では、newの引数になにを与えているかというと、無名ハッシュを与えています。
'bag'={} の部分は、多段ハッシュになっています。
これをnewに与え、$humanによりHumanパッケージの関数を呼べるようにします。
$human->push('やくそう');
お次は、
38 $human->push('やくそう');
この部分を説明したいと思います。
push関数自体は、
15 sub push { 16 my $self = shift; 17 my $item = shift; 18 $self->{'bag'}->{$item}++; 19 }
このようになっています。
注目すべきは、 16行目の $self です。
$selfに第1引数を受け取っています。
第1引数は、
38 $human->push('やくそう');
の形で呼び出しているので、 $human です。 (※ ->で関数を呼ぶ方法を思い出してください)
$humanは、
33 my $human = Human::new({'name' => 'ビンゴ', 34 'age' => 1, 35 'bag' => {} 36 });
のように作っています。
ですから、
$human = {'name' => 'ビンゴ', 'age' => 1, 'bag' => {} }
なのです。ただし、普通のハッシュと違うところは、このハッシュは、Humanパッケージの関数を呼べるのです。
そんな$humanを$selfに代入しているのが、16行目とういことになります。
17行目の、
17 my $item = shift;
で、$itemに 'やくそう' が代入されています。
18行目の
18 $self->{'bag'}->{$item}++;
が、ミソです。
ここで使われている -> は、両方ともデリファレンスの矢印です(多段ハッシュ)。
ハッシュにキーが登録されていない場合にいきなり++すると、値が1となります。
次回は、1を++するわけですから2になります。
次のソースを試してみてください。
use strict; my $c = {}; $c->{'hoge'}++; print $c->{'hoge'}, "\n"; $c->{'hoge'}++; print $c->{'hoge'}, "\n";
これで、$selfの'bag'が示すハッシュの内容が変わりました!
ここで、$selfは$humanであったことを思い出しましょう!
$humanは、
$human = {'name' => 'ビンゴ', 'age' => 1, 'bag' => {} }
ですね。つまり、リファレンスなのです。
そうです。 $humanは、リファレンス渡しになっているのです。
ですから、$selfの内容をいじると、$humanの値もかわります。
38 $human->push('やくそう');
こう書けば、$humanの内容が書き変わっているのです。
これは、JavaScriptやJavaなどの他のオブジェクト指向言語のメソッドで、できることと同じです。
ただ、他の言語で this にあたるような部分を第1引数として受け取って $self に代入しているのです。
$human->get_old();
さて、なんでいっぱい説明のいる push関数から説明してしまったのでしょうか。
43 $human->get_old();
の部分を見てみましょう。
get_old関数自体は、
21 sub get_old { 22 my $self = shift; 23 $self->{'age'}++; 24 }
このようになっていますね。
$selfには$humanが入っています。その$humanの年を+1しているのです。
$human->check_age();
check_age関数は受け取ったハッシュの'age'キー(年)を表示しているだけです。
26 sub check_age { 27 my $self = shift; 28 print $self->{'age'}, '才', "\n"; 29 }
43行目と44行目で理解を深める。
43 $human->get_old(); 44 $human->check_age();
43行目のget_oldで $self->{'age'}++ されています。
$selfにはリファレンスが渡っているので、実質的に、
$human->{'age'}++ されたのと同じです。
とても信じられないと言うのなら、
44 $human->get_old(); 45 print "-------------\n"; 46 print Dumper $human; 47 print "-------------\n"; 48 $human->check_age();
とソースを書き換えて、$humanを表示させてみるとよいでしょう。
あ! Data::Dumperを使うので、
2 use Data::Dumper;
と 2行目に Data::Dumper; を付け加えることを忘れないでください。
$humanを表示させている部分だけをみてみる
$humanを表示させた部分を載せておきます
------------- $VAR1 = bless( { 'bag' => { '肉' => 1, 'やくそう' => 2 }, 'name' => 'ビンゴ', 'age' => 2 }, 'Human' ); -------------
このようになっています。
$humanをリファレンス渡しで push やら、 old_ageやらに渡しているので、いろいろ値が書き変わっているのがわかります。
簡単に書くと、
$VAR1 = bless ( { $humanの内容 }, 'Human' );
という形をしていますね。
本当にData::Dumperはすばらしいです。blessの第2引数に 'Human'が与えられている形をしていますね。
まったくそのまんまの通りです。 $humanは、Humanパッケージの関数が呼べることを、Data::Dumperは教えてくれているのです。
$selfで頑張ってメソッドを作成する
このように、Perlのオブジェクト指向は、$selfに第1引数をもらうことにより記述していきます。
$selfという名前にこだわらず、変数名は何にしてもいいのですが、 $selfを使っているソースが非常に多いです。
newに別の内容の無名ハッシュを与えてみる
newに別の内容の無名ハッシュ、例えば、
{'name' => '木村', 'age' => 5, 'bag' => {} }
といったものを与えて、
package main; my $bingo = Human::new({'name' => 'ビンゴ', 'age' => 1, 'bag' => {} }); my $kimura = Human::new({'name' => '木村', 'age' => 1, 'bag' => {} }); $bingo->push('やくそう'); $kimura->push('肉');
といったように、別の持ち物を持たせていくことも可能です。
$bingoと$kimuraは別々のハッシュなので、ただ別々のハッシュの内容が変わっていくだけです。
まとめ
このように、Perlのオブジェクト指向は、newにリファレンスを与えることからはじまり、
返ってきた内容を、 ○->hoge() というように -> によって知らないうちに第1引数となったモノを$selfで受け取り、その$selfの内容をがちゃがちゃ書き換えていくことにより、成り立っています。
$selfは他の言語の this みたいなものであると考えると非常にわかりやすいと思います。
逆に、他の言語で this がわからないなぁ という段階なら、 $self の考え方で パッと理解できるかもしれません。
this というのは、そのメソッドを呼んだやつ自身のことですね。
この自身という意味合いで、Perlでは、 $self が使われています(たぶん)。
それでは また〜
追記
参考
古い記事で、今よりももっと理解が曖昧だったときに書いた記事ですが、
参考までに以下の記事をあげて置きます。当時の私がどのように理解していったか、またどのように混乱していたかもわかると思います。
ビンゴ中西のほげほげKansai.pm第8回ミーティングに行ってきました@荷造り中
ビンゴ中西のほげほげPerlでのオブジェクト指向の前に
ビンゴ中西のほげほげPerlでオブジェクト指向を書いてみよう