構造化データはアクセスが難しい(忘れてしまう)
- 前置き長いです...。オブジェクト指向に慣れている方はこちらへスキップして下さい。
- 以前書いたPerlプログラミングに関する"レポート"にて「Perlは構造化データを作りやすい」ことを説明しました。例えば下記モデルがあるとします。"top"モジュールの下に"A"というインスタンス名で"sub_a"モジュールを置いています、それぞれのポートをワイヤで結んでいるイメージです。
+-top-------------------------------------+
| +-sub_a A---+ |
|in_0--in_a0--|in_0 out_0|--out_a0--out_0|
|in_1--in_a1--|in_1 | |
| +-----------+ |
+-----------------------------------------+
- このデータ構造を表現すると、例として下記のようになるでしょう。
$obj->{name}='top';
->{ports}->['in_0', 'in_1', 'out_0'];
->{wires}->{in_a0}->['in_0', 'A.in_0'];
->{in_a1}->['in_1', 'A.in_1'];
->{out_a0}->['out_0', 'A.out_0'];
->{instances}->{A}->{name}='mod_a';
->{ports}=['in_0', 'in_1', 'out_0'];
...
- このデータをたどれば接続関係はわかります。例えばワイヤ"in_a1"の接続先は下記のように出力できます。
for (my $i=0; $i<@{$obj->{wires}->{in_a1}}; $i++) {
print $obj->{wires}->{in_a1}->[$i],"\n";
}
- ですが、スクリプトを作っている最中や作ったばかりなら良いですが、しばらく時間が経ってから何かをしようとした場合、データ構造を忘れてしまっています。対応として、処理がシンプルになる関数を作ることでしょう。
my @connections = getConnections($obj, 'in_a1');
- 上記からわかるように、構造化データとアクセス/処理関数は密接に関わっているので、セットで扱うべきものです。セット...そうですね...「同じ仲間」に入れる...「クラス」とすれば便利そうです。
- Perlで「同じ仲間/クラス」として関数をまとめる仕組みは package です。一般的に名前空間(name space)と呼ばれるものです。'use パッケージ名;'って見たことありますよね。
- packageの関数は処理対象データと密接に関わっているので、データ(オブジェクト)をどのpackageで扱う想定かわかれば適用関数も明確になります。このときデータ(オブジェクト)を中心にしてプログラミングするので、まさに「オブジェクト指向プログラミング(OOP:Object Oriented Programming)」になります。
- 最初からOOPを念頭に置いた言語では、データ(オブジェクト)と関数を紐づける仕組みとして'Class'を持っています。PerlはClassを持ちませんがオブジェクトにpackage名情報を付ければ「クラス」として機能します。(*1)
- PerlでOOPするために、オブジェクトにpackage名情報を追加する仕掛け...それが'bless'です。これによって'package'を「クラス」として扱うことができるようになります。
Perlのクラスは処理対象オブジェクトにpackage情報を与えたもの
- 少し強引かもしれませんが、オブジェクト指向プログラミング(OOP)におけるクラスとは関数と処理対象データを仲間としてまとめたものと考えることができます。
- Perlではpackageを使用すれば関数群をまとめることができます。そしてpackageの仲間としてデータのオブジェクトを加えるにはblessを使用します。これによってpackageをクラスとして扱うことが可能になります。下記が記述例(BuildVerilogクラス)です。
#!perl -w
package BuildVerilog; # パッケージ名/クラス名
use strict;
# ==============================================================================
# コンストラクタ
sub new {
my $obj = {}; # 無名ハッシュのリファレンス
bless $obj, __PACKAGE__; # __PACKAGE__は所属package名を持つ特殊変数
return $obj; # blessされた$objを戻す
}
1; # package最終行に付けるPerlの慣習。true値を返す。無いとコンパイルエラーになる。 |
- 上記のBuildVerilogクラスを使用するには、下記のようにコンストラクタを実行します。コンストラクタもpackage関数の一つなのでnewである必要性はありませんが、一般的にはnewと名付けるのが普通です。
#!perl -w
use lib './';
use BuildVerilog;
use strict;
{
my $obj = BuildVerilog->new(); # BuildVerilogに属するデータのオブジェクト
} |
メソッドの追加と使用
- それでは、BuildVerilogクラスにメソッド(setModuleName, getModuleName)を追加してみましょう。とは言ってもpackageに関数を追加するだけですが、引数の定義に注目して下さい。先頭が処理対象のデータオブジェクトになっています。
#!perl -w
package BuildVerilog; # パッケージ名/クラス名
use strict;
# ==============================================================================
# コンストラクタ
sub new {
my $obj = {}; # 無名ハッシュのリファレンス
bless $obj, __PACKAGE__; # __PACKAGE__は所属package名を持つ特殊変数
return $obj; # blessされた$objを戻す
}
# ==============================================================================
# モジュール名設定
sub setModuleName {
my ($obj, $top_module_name) = @_; # 先頭が$objであることに注目
$obj->{name} = $top_module_name;
}
# ==============================================================================
# モジュール名取得
sub getModuleName {
my ($obj) = @_;
return $obj->{name};
}
1; # package最終行に付けるPerlの慣習。true値を返す。無いとコンパイルエラーになる。 |
- メソッド呼び出しはオブジェクトにアロー演算子を使用します。オブジェクトがクラス名/package名情報を持っていることがわかりますね。そして呼び出し元オブジェクト自身が「1番目の引数」としてメソッドに渡されることがポイントです。
#!perl -w
use lib './';
use BuildVerilog;
use strict;
{
my $obj = BuildVerilog->new(); # BuildVerilogに属するデータのオブジェクト
$obj->setModuleName('top'); # setModuleName()呼び出し
print $obj->getModuleName(); # getModuleName()呼び出し
} |
インスタンス
- コンストラクタで戻るオブジェクトは処理対象データのリファレンスなので、もう一度コンストラクタを実行すれば別のリファレンス、つまり別のインスタンスとして振る舞います。
- 例として下記コードを見て下さい。モジュール名が別々にセットされていることがわかります。
#!perl -w
use lib './';
use BuildVerilog;
use strict;
{
my $obj_0 = BuildVerilog->new(); # インスタンス0
my $obj_1 = BuildVerilog->new(); # インスタンス1
$obj_0->setModuleName('top_0'); # インスタンス0 モジュール名設定
$obj_1->setModuleName('top_1'); # インスタンス1 モジュール名設定
print 'インスタンス0: ',$obj_0->getModuleName(),"\n"; # インスタンス0 モジュール名表示
print 'インスタンス1: ',$obj_1->getModuleName(),"\n"; # インスタンス1 モジュール名設定
} >runme.pl
インスタンス0: top_0
インスタンス1: top_1 |
クラスを積極的に使いましょう
- Perlのクラスは、処理対象データオブジェクトにblessでpackageの名前情報を与え、関数とセットにして使えるようにしているだけです。かなりシンプルです。
- クラス一般の利点ですが、複雑なデータ構造を隠蔽して使いやすく作ることができます。また、途中でデータ構造自身をリファクタリングすることも可能です。
- また今回紹介していませんが演算子オーバーロードも可能です。複雑な構造のデータを直感的に操作できるようになります。
- 似たようなツールやアプリケーションを複数個作成する場合など、ベース部分をクラスにしておくと便利に使えます。積極的な利用をお勧めします。
|