Perlオブジェクト基礎文法最速マスター

Perl初心者のためのPerlノートです!

HTML/ CSS/ CGI-Perl/ JavaScript/ JavaApplet/ AccessUp/ Internet/ EnglishLearn/ ちゃいちゃん天使/ 天使メッセージ/ 飯田ワールド/ 結城ワールド/ プロフィール/ WEB相談室/ WEBアンテナ/ WEBリーダー/ 燈明日記/ yahoo

◆ はじめに

本ページは、私がPerlを習得した時(いや習得中)のノートです。

ごく普通の言語(COBOL,C,Java,VB等)を知っている人が、Perlをやり始めるといろいろと悩むことがあります。

それは、

です。

本ページは、そのようなハードルを一つ一つクリアにしていけたらと思います。

そして、本ページが何かのお役に立てれば幸いです。では、ごゆっくりご覧ください。

尚、ご感想、ご意見、誤字、脱字、間違い等がありましたら遠慮なくPerlノート掲示板へご指摘ください。

◆ サイト最新情報

◆ コンテンツ

2010-02-06 Perlオブジェクト基礎文法最速マスター

Perl5でのオブジェクトの基礎を解説したいと思います。

Perlでのオブジェクト指向と言えば、モダンPerl系ではMooseモジュール等なのですが、今回は、あくまでも基礎編です。


Perlで最速にオブジェクトをマスターするには、まず、「リファレンス」と「use宣言」と「bless」の3つが理解できていないとなりません。


なぜかと言うと、Perlでのオブジェクトはblessされたリファレンスなのです。また、Perlでのクラスはモジュールなのです。そのモジュールでの一番の謎は、use宣言なのです。


なので、はじめにこの3つを理解してから、そのあと「オブジェクト」と「オブジェクト指向」について解説していきます。

つまり、以下の順番です。



リファレンスについて

普通、リファレンスといえば、一覧の機能参照のことですが、Perlでリファレンスといえば、言語仕様のアドレス参照のことなのです。

まぁ、PerlでのリファレンスをC言語でいえば、アドレス演算ができないポインタのようなものなのです。


たとえば、配列変数 @array のリファレンス(参照)は、 \演算子で取得することができます。

以下は、配列変数 @array のリファレンスをスカラー変数 $array_ref1 へ格納しています。

my @array = (1,2,3);
print $array[2], "\n"; # 3番目の要素を表示
my $array_ref1 = \@array; # 配列変数のリファレンスをスカラー変数へ格納

つぎにリファレンスから元の値を求めたい時は、デリファレンスすると言います。

デリファレンスには、参照先の変数の識別子を先頭に付け足す方法と矢印演算子『->』を使う方法の2通りがあります。

print $$array_ref1[2], "\n";  # 3番目の要素を表示;
print $array_ref1->[2], "\n"; # 3番目の要素を表示

リファレンスの利用場面は、私が思いつくところでは以下のケースです。


◆多次元配列を処理するケース
my $array_ref1 = \@array; 

配列変数の@の前に、\をつけると、配列のリファレンスが取得できます。

上記は、配列@arrayのリファレンスを$array_ref1へ格納しています。

また、無名配列のリファレンスを格納することもできます。

my $array_ref2 = [ 1, 2, 3, 4 ]; 

リストを [ ] でくくると、無名配列になり、そのリファレンスを$array_ref2へ格納しています。


つぎに、リファレンスから元の値を取得することをデリファレンスといいます。

my $array_ref2 = [ 1, 2, 3, 4 ]; 
print $array_ref2->[3]; #デリファレンス

ちなみに、『->』は矢印演算子といい、リファレンスをデリファレンスします。


補足:リファレンスの元をリファレントいいます。

つまり、$array_ref1がリファレンスで、\@arrayがリファレントです。


この機能を利用すると多次元配列の処理が可能です。

以下は、配列変数 @a の要素に配列のリファレンスを各々格納していて、結果的に2次元配列にしています。

@a = ( [ qw(00 01 02) ],
       [ qw(10 11 12) ],
       [ qw(20 21 22) ]);
for $i (0 .. $#a) {
    for $j (0 .. $#{$a[$i]}) {
        print '$a[' . $i . '][' . $j . ']=' . $a[$i][$j] . '   ';
    }
    print "\n";
}

尚、上記では『->』矢印演算子がありませんが、大括弧(ブラケット)や中括弧(ブレース)の間にはさまれた矢印は省略可能なのです。


◆関数をリファレンスでコールするケース

関数のリファレンスも配列と同様にできます。

sub01("aaa");   # 普通にコール
$sub = \&sub01;
$sub->("aaa");  # リファレンスでコール
sub sub01 {
    print "--- sub01 $_[0]---\n";
}

『$sub = \&sub01;』で関数のリファレンスを格納して、『$sub->("aaa")』でコールしています。


◆関数の引数へ配列を渡すケース

Perlでの関数の引数はリストです。

引数に配列変数を渡すとリストに展開されてしまい、普通は上手く配列変数を渡せません。

そこで登場してくるのが配列変数のリファレンスです。

リファレンスなら、上手く配列変数を渡せるのです。

my @bbb = ("bbb", "ccc", "ddd");
my @xxx = ("xxx", "yyy", "zzz");
sub02("aaa", \@bbb, \@xxx);  
sub sub02 {
    print "--- sub02 $_[0]---\n";
    print "--- sub02 $_[1]->[0]---\n";
    print "--- sub02 ${$_[1]}[1]---\n";
    print "--- sub02 $_[1][2]---\n";
    print "--- sub02 $_[2][0]---\n";
    print "--- sub02 $_[2][1]---\n";
    print "--- sub02 $_[2][2]---\n";
}

◆ 関数のリターン値にするケース

関数のリターン値でblessしたリファレンスが返された場合、それは、他言語でいうオブジェクトと同じ感じになるのです。

my $date = DateString->new();
print $date->to_string, "\n";

package DateString;
sub new {
    my $class = shift; # 第一引数は、クラス名が渡される。
    my $time = shift || time();
    return bless { time => $time }, $class; # blessしたリファレンスを返す。
}
sub to_string {
    my $self = shift;
    return scalar localtime $self->{time};
}


use宣言について

Perlをやり始めると、requireはすぐ分かるんだけれど、useって謎だなぁって思いますね!

今回は、そんなuseの謎に迫ります。


はじめに、requireは、C言語でいう%includeと同じで別ファイルにあるソースを実行時にあたかも自ソースとして読み込みます。

つぎに、useは、実行前(コンパイル時)にあたかも自ソースとして読み込みます。


実行前というのは、Perlではコマンド起動時にコンパイルしてから実行するという2段階方式になっているのです。

つまり、useはコンパイルに実行され、requireはコンパイルに実行されるのです。


そして、読み込んだソース(モジュール)にpackage宣言がない場合、useとrequireでは、これ以外の違いはありません。

しかし、モジュールにpackage宣言があると、ちょっとややっこしくなるのです。


まず、モジュールでなく、メインソースの方には、package宣言がありませんが、実は、デフォルトでmainというpackage名になっています。

つまり、逆に言うと、package宣言のないモジュールは、mainモジュールなのです。

一方、モジュールの方は、package宣言でpackage名をmain以外で指定すると、当然ですがメインソースとは別のpackage名になりますね。


しかし、別のpackage名になっても、useには他モジュールを、あたかも自モジュール(mainモジュール)にあるようにする仕組みがあるのです。

たとえば、以下のサンプル mainモジュールとModuleモジュール をご覧下さい。

いきなり、importが出てきますが、これは、Moduleモジュール内でExporterモジュールを継承していて、Exporterモジュール内のメソッドなのです。


◆サンプル

・mainモジュール

#use Module;
BEGIN { require Module; Module->import;} # use Module; と等価
print sumx(5,3),"\n"; # Moduleモジュールのsumx関数があたかも自関数のように使える

・Moduleモジュール

package Module;
use base Exporter;          # use Exporter; our @ISA = qw(Exporter); と等価
our @EXPORT = qw(&sumx);
sub sumx {
 return $_[0]+$_[1]; # 渡された引数2つを加えて返す処理
}
1;

◆補足

Moduleモジュールでは、『use base Exporter』でExporterを継承して、『our @EXPORT = qw(&sumx);』でsumx関数をエクスポートしています。

mainモジュールでは、『Module->import;』でModuleモジュールでエクスポートしたsumx関数をインポートしています。



blessについて

useやモジュールとリファレンスが理解出来てくると、blessが気になってきますね!

今回は、そんなblessの謎に迫ります。


blessを一言でいうと、リファレンスとパッケージを紐付けます。


たとえば、以下を見てください。

use strict;
use warnings;

my %test_HASH = (aaa => 'XXX', bbb =>'YYY');
my $test = bless \%test_HASH, 'package01';  # リファレンスとパッケージを紐付け

print '-->', $test->{aaa}, '<--', "\n";
$test->test01(3);
print '-->', $test->{aaa}, '<--', "\n";

package package01;
sub test01 {
    print "第1引数は$_[0], 第2引数は$_[1]です。", "\n";
    my $obj_self = shift;
    $obj_self->{aaa} = 'ZZZ';
}

『my $test = bless \%test_HASH, 'package01';』で、%test_HASHのリファレンスとpackage01のパッケージを紐付けています。

そして、blessのりターン値が%test_HASHのリファレンスなのですが、だだのリファレンスでなく、blessで紐付けられたpackage01パッケージも含まれるのです。

なので、『$test->test01(3);』のようにpackage01パッケージのtest01サブをコールすることが出来るのです。


コールする時、『->』矢印演算子を使うとtest01サブでは、自動的に第1引数が自リファレンスになるのです。

なので、『$obj_self->{aaa} = 'ZZZ';』でリファレンスからもとのハッシュ変数にアクセスして書き換えが可能なのです。


結果的にリファレンスが、ハッシュ変数(プロパティ)とサブルーチン(メソッド)を持つことになるのです。

プロパティとメソッドと言えば、そう、オブジェクトになるのですね…これが!!



オブジェクトについて

えーと、Perlでは一つのパッケージが一つのクラスになります。

つまり、パッケージ名がクラス名になるのです。


また普通、クラスには、オブジェクトを構築するコンストラクタがあります。

コンストラクタは、blessでリファレンスをクラス名に紐付けて、リターン値にします。

コンストラクタの呼び出し側では、リターン値を変数に格納して、インスタンスとします。


って、文章で説明しても分かり辛いので、以下のソースを見て感じとってください。

use strict;
use warnings;

print "\n", '◆ポイント1の例(パッケージ名(クラス名)が第1引数)', "\n";
package00->test00(3); # クラス名に矢印演算子で関数をコールする

package package00;
sub test00 {
    print "第1引数は$_[0], 第2引数は$_[1]です。", "\n";
    my $class = shift;
    print "第1引数は $class でクラス名になります。\n"
}


print "\n", '◆ポイント2の例(blessリファリンス(オブジェクト)が第1引数---', "\n";
my %test_HASH = (aaa => 'XXX', bbb =>'YYY');
my $test = bless \%test_HASH, 'package01';

print '-->', $test->{aaa}, '<--', "\n";
$test->test01(3); # インスタンスに矢印演算子で関数をコールする
print '-->', $test->{aaa}, '<--', "\n";

package package01;
sub test01 {
    print "第1引数は$_[0], 第2引数は$_[1]です。", "\n";
    my $obj_self = shift;
    $obj_self->{aaa} = 'ZZZ';
    print "第1引数は $obj_self  でオブジェクトになります。\n"
}


print "\n", '◆ポイント3の例 オブジェクト例(委譲)---', "\n";
my $obj_package02 = package02->new(); # newはオブジェクトコンストラクタでこれを起動(委譲)する
                                      # $obj_package02にはインスタンスが格納される
print '-->', $obj_package02->{bbb}, '<--', "\n";
$obj_package02->test02(4);            # オブジェクトのメソッドを起動する
print '-->', $obj_package02->{bbb}, '<--', "\n";

package package02; # パッケージは、Perlではクラスになる
sub new {
    print "第1引数は$_[0]でクラス名です。", "\n";
    my $class = shift;
    my %test_HASH = (aaa => 'XXX', bbb =>'YYY');
    return bless \%test_HASH, $class;
}
sub test02 {
    print "第1引数は$_[0], 第2引数は$_[1]です。", "\n";
    my $obj_self = shift;
    $obj_self->{bbb} = 'ZZZ';
    print "第1引数は $obj_self  でオブジェクトになります。\n"
}


オブジェクト指向について

Perlは元々は、オブジェクト指向言語ではありません。

しかし、柔軟な言語仕様のPerlでは、オブジェクト指向言語のように振る舞うことも可能なのです。


普通、オブジェクト指向プログラミングでは、以下の要素が必要です。


ということで、カプセル化とオーバーロード以外は、大丈夫なのです。

以下に、クラス、コンストラクタ、継承、多態性、オーバーライドを使用したサンプルを書いてみました。


◆サンプル
use strict;
use warnings;

my $obj;
my $hiki = shift;

if ($hiki eq '1') {
    $obj = Otokonoko->new();
}
else {
    $obj = Onnanoko->new();
}
print $obj->getName(), "\n";
$obj->shumi();


package Person;
sub new {
    my $class = shift;
    my $self = {
        Name => $class,
    };
    return bless $self, $class;
}
sub getName {};


package Otokonoko;
use base 'Person';
sub getName {
    my $self = shift;
    return $self->{Name};
}
sub shumi {
    print "バスケ\n";
}


package Onnanoko;
use base 'Person';
sub getName {
    my $self = shift;
    return $self->{Name};
}
sub shumi {
    print "ケーキ作り\n";
}

◆実行結果
C:\perltest>perl tatai.pl 1
Otokonoko
バスケ

C:\perltest>perl tatai.pl 2
Onnanoko
ケーキ作り



たぶん相互リンク

◆ おすすめ Perl本

■ 続・初めてのPerl 改訂版

『初めてのPerl』の次ぎに読むと吉です。

結構いいですが・・・、初心者にはちょっと難しいかもです。

■ 結城浩のPerlクイズ

この本には、ちゃいちゃんパパの小話しが載っています(謎)。

ある程度、Perlを理解している人には、たまらない内容になっています。さすが結城先生だな!

結城浩のPerlクイズ

■ 新版Perl言語プログラミングレッスン入門編

実は、私はまだ読んでいませんが、結城先生の本なので良くないはずがありません。

新版Perl言語プログラミングレッスン入門編

■ CGI&Perlポケットリファレンス (Pocket reference)

この本は、非常に役に立ちます。ちょっと調べるのに最適です。サンプルも説明も的を射ています。

CGI&Perlポケットリファレンス (Pocket reference)

■ Perlベストプラクティス

一読の価値有りだと思います。

Perlベストプラクティス

■ プログラミングPerl〈VOLUME1〉

ご存知、Perl本の聖書、ラクダ本(上)です。

■ プログラミングPerl〈VOLUME2〉

ご存知、Perl本の聖書、ラクダ本(下)です。

◆ おわりに

最後に、本ページが、何かのお役に立てれば幸いです。

尚、ご感想、ご意見、誤字、脱字、間違い等がありましたら遠慮なくPerlノート掲示板へご指摘ください。

HTML/ CSS/ CGI-Perl/ JavaScript/ JavaApplet/ AccessUp/ Internet/ EnglishLearn/ ちゃいちゃん天使/ 天使メッセージ/ 飯田ワールド/ 結城ワールド/ プロフィール/ WEB相談室/ WEBアンテナ/ WEBリーダー/ 燈明日記/ yahoo

人のよいところをどんどん見つけよう