2011-03-29
■[Perl] 無名ハッシュコンストラクタのブレースの前に+を付けないといけないケース
気になったので調べてみた。
perlrefを見れば一発だった。
そもそも+を付けるって何
中かっこは、BLOCK を始めとして他のことにも使われますから、 開きの中かっこが BLOCK の開始でないことを Perl に教える ために文の最初の中かっこの前に + や return をつけて、 曖昧さをなくすようにする必要がある場合があります。
perlref - Perlのリファレンスとネストしたデータ構造 - perldoc.jp
どんなときに+を付けないといけないのか
perlrefのサンプルそのままなんだけど一応。
# sample.pl use strict; use warnings; use Data::Dumper; sub hashem { { @_ }; # ハッシュリファレンスを返したい } print Dumper hashem( qw/ a 1 b 2 / );
$ perl sample.pl $VAR1 = 'a'; $VAR2 = '1'; $VAR3 = 'b'; $VAR4 = '2';
ハッシュリファレンスを返すつもりが、配列が返される。
では、+を付けて実行すると
use strict; use warnings; use Data::Dumper; sub hashem { +{ @_ }; # ハッシュリファレンスを返したい } print Dumper hashem( qw/ a 1 b 2 / );
$ perl sample.pl
$VAR1 = {
'a' => '1',
'b' => '2'
};
想定通りに、ハッシュリファレンスが返される。
なるほど、納得!
参考
■[Perl] 続・はじめてのPerl 11章
11章 オブジェクト入門
11.1 動物と話すことができたら
use strict; use warnings; sub Cow::speak { print "a Cow goes moooo!\n"; } sub Horse::speak { print "a Horse goes neigh!\n"; } sub Sheep::speak { print "a Sheep goes baaah!\n"; } my @pasture = qw/ Cow Cow Horse Sheep Sheep /; { no strict 'refs'; for my $beast ( @pasture ) { &{$beast . '::speak'}; # シンボリックコードレフ } }
シンボリックコードレフは読みにくい
他の方法はないのか?
11.2 メソッド呼び出しの矢印
Classとは
同じような動作と特性を持つものの集まり
Classパッケージ内のmethodサブルーチンを呼び出す方法
Class->method
メソッドとは
サブルーチンのオブジェクト指向バージョン
# メソッド呼び出しの矢印を使う
Cow->speak;
Horse->speak;
Sheep->speak;
# さらに以下のように書ける my @pasture = qw/ Cow Cow Horse Sheep Sheep /; for my $beast ( @pasture ) { # シンボリックコードレフを使う必要がなくなった $beast->speak; }
OOPの原則
共通コードを最小限に減らすこと
コーディング量の削減による時間の節約
テストとデバッグの効率化による時間の節約
11.3 メソッド呼び出しの特別な引数
矢印を使った、メソッド呼び出し
Class->method( @args );
これは、以下のようにClass::method を呼び出すこととなる
Class:method( 'Class', @args );
第一引数として、クラス名を受け取る
@argsが指定されていない場合、クラス名だけを引数として受け取る
# 第一引数を使うことで、以下のようにメソッドを定義できる use strict; use warnings; sub Cow::speak { my $class = shift; print "a $class goes moooo!\n"; } sub Horse::speak { my $class = shift; print "a $class goes neigh!\n"; } sub Sheep::speak { my $class = shift; print "a $class goes baaah!\n"; } my @pasture = qw/ Cow Cow Horse Sheep Sheep /; for my $beast ( @pasture ) { $beast->speak; }
11.4 第二のメソッドを呼び出して話を単純化する
前節のコードは、同じ構造のコードを多数含む
どのように単純化するべきか
# use strict; { # Animalクラスを作成 # speakメソッドを定義する package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Cow; @ISA = qw/ Animal /; sub sound { 'moooo' } } Cow->speak;
Cow->speak; の処理の流れ
1. 引数リストを作る
上記の場合、Cowのみ
2. Cow::speakを探す
しかし、Cowにはspeakメソッドはない
3. 継承配列の@Cow::ISAをチェックする
@Cow::ISAは実在し、Animalという名前を持つ
4. Animalの中に、speakメソッド(Animal::speak)があるかチェックする
speakを見つけることができる
5. 引数リストを使い、メソッドを呼び出す
以下のコードを実行することと同じことになる
Animal::speak( 'Cow' );
6. Animal::speakメソッド内で、$classにCowが格納される
7. $class->sound の呼び出しで、Cow->soundが呼び出される
8. "a Cow goes moooo!" という出力が得られる
11.5 @ISAについての注意
@ISA
「イズア」と発音
Cow is a Animal ということを宣言する
実際には、言語学用語
複数の親クラスを検索することがありえるので、配列
Animalも@ISAを持っている場合、それも検索対象
検索は、再帰的で深さ優先
@ISAの左の要素から右の要素へ検索が行われる
一般に@ISA配列の要素は1つだけ
多重継承がされている場合、複数になる
use strictを有効にすると、@ISAについて警告される
@ISAが、明示的なパッケージ名を含む変数でなく、レキシカル変数でもないため
@ISAは、レキシカル変数にすることは不可能
継承メカニズムにより見つけられるパッケージに属していなければならない
@ISAの宣言と設定方法
最も簡単な方法:パッケージ名を書く
@Cow::ISA = qw/ Animal /;
Perl5.6以降では、our宣言を使うことができる
package Cow;
our @ISA = qw/ Animal /;
暗黙に名前を与えたパッケージ変数として扱う
package Cow;
use vars qw/ @ISA /;
@ISA = qw/ Animal /;
これは、use base を使うことで以下のように書ける
package Cow;
use base qw/ Animal /;
use baseはコンパイル時に実行される
11.6 メソッドのオーバーライド
use strict; { # Animalクラスを作成 # speakメソッドを定義する package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Mouse; use base qw/ Animal /; sub sound { 'squeak' } sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; print "[but you can barely hear it!]\n"; } } Mouse->speak;
Mouseは独自のspeakメソッドを持っている
Animalクラスのspeakメソッドを呼び出すわけではない
これをオーバーライドという
オーバーライド
基底クラス(Animal)のメソッドの代わりとなる専用のメソッドを持っている場合に、
派生クラス(Mouse)のメソッドを呼び出すために使われる
しかし、上記のMouse::speakは、Animal::speakのコードと同じ箇所がある
Animal::speakに変更が発生した場合、
Mouse::speakにも手を加えなければならない可能性がある
OOPの原則
コードの再利用はコピペではなく、継承で行う
# よりOOPらしいコードに use strict; { package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Mouse; use base qw/ Animal /; sub sound { 'squeak' } sub speak { my $class = shift; Animal::speak( $class ); # 矢印によるメソッド呼び出しではない!! print "[but you can barely hear it!]\n"; } } Mouse->speak;
矢印によるメソッド呼び出しを使わない理由
矢印を使うと、Animal::speakの第一引数は、"Animal"になってしまう
soundメソッドを呼び出すときに、オブジェクトに合ったメソッドが呼び出せない
しかし、Animal::speakを直接呼び出すと、また別の問題が起こる
矢印によるメソッド呼び出しを使わなかった場合に起こる問題
継承関係の解決が行われない
呼び出そうとしたメソッドが継承されたメソッドだった場合、プログラムは異常終了する
よって、上記のコードの方法は正しくないやり方
11.7 異なる場所からの検索の開始
use strict; { package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Mouse; use base qw/ Animal /; sub sound { 'squeak' } sub speak { my $class = shift; $class->Animal::speak( @_ ); print "[but you can barely hear it!]\n"; } } Mouse->speak;
上記のコードは正しく動作する
Animalでspeakを探す
見つからない場合は、Animalの継承チェーンをたどりspeakを探す
第一引数は、$classとなる
この方法の問題点
@ISAと最初に検索するパッケージを同じものにする手作業が入る
@ISAに複数のエントリがある場合、どちらがspeakを定義しているのか不明な場合がある
11.8 SUPERなやり方
use strict; { package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Mouse; use base qw/ Animal /; sub sound { 'squeak' } sub speak { my $class = shift; $class->SUPER::speak( @_ ); # SUPERクラスを使う print "[but you can barely hear it!]\n"; } } Mouse->speak;
Animalクラスを、SUPERクラスに書き換えることで、
@INCにエントリーされているクラスから、speakが検索されるようになる
@INCの中で複数のspeakが定義されている場合
最初に見つかったものが呼び出される
11.9 @_の処理
親クラスの動作に何か処理を追加している場合
@_を引数として渡すべき
$class->SUPER::speak( @_ );
親クラスの動作を正確に制御するような場合
明示的に引数リストを見定めて、渡すべき
- 作者: Randal L. Schwartz,brian d foy,Tom Phoenix,吉川英興,伊藤直也,田中慎司,株式会社ロングテール/長尾高弘
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2006/10/21
- メディア: 大型本
- 購入: 9人 クリック: 389回
- この商品を含むブログ (99件) を見る
