技術的雑談-hashとポインターで簡易DBを作ろう
環境
perl5.6以降の環境なら多分どこでも。
(いや、perl5以上ならどこでも平気かもしれない…。)
目的
hash(連想配列)を使って、構造化した簡易データベースもどきを作る。
内容
hashはperlの基本機能で、「キー」に対応した「値」を複数格納できる一種の配列変数です。
キーは文字列ですが、値には「undef」を含む全ての値が入れられます。
変数名の先頭に「%」をつけたものがhashになります。
# 簡単なhashの例 use strict; my %test; # hashの宣言 $test{'key1'} = "abcdef"; # 新しいキー「key1」を作成し、値に「abcdef」を入れる $test{'hogehoge'} = 123; # 新しいキー「hogehoge」を作成し、値に123を入れる # 上の3行は以下のようにも書ける # my %test = ( 'key1' => "abcdef", 'hogehoge' => 123); # 私はこっちのほうが好きだったりします
しかし、このままでは「1つの属性名について値は1つまで」ということになってしまいます。
例えば、あるLogin IDについて「パスワード」と「最終アクセス日」の2つを管理したい場合、hashを2つ用意してやる必要があります。
# Login IDのpasswordと最終アクセス日を保持する my %password; # パスワードhash my %access; # 最終アクセス日時 $password{'user1'} = 'xxxxxx'; $access{'user1'} = '2006/10/23';
この方法でももちろん良いのですが、例えばこのパスワードとアクセス日を両方セットで更新するメソッドを作るとすると、メソッドの引数に2つのhashを渡さなければなりません。
これは何となく使いづらいですね…。
で、どうするかというと、「hashの値には何でも入れられる」ということを利用します。
perlでは各種の変数にそれに対応する「ポインタ型」があります。
ポインタは単に変数名の前に「\(バックスラッシュ、又は円マーク)」を付ければ良いです。
(ポインタとは、変数の値そのものではなく、「変数の記憶されている位置」を表すスカラ変数です。)
# ポインタの例 my $a = '1234'; my $b = \$a; # $bは変数$aの記憶されている位置を表す $$b = '5678'; # ポインターの前に「$」をつけることによって、ポインタの指す位置の変数にアクセスできる print "aの値 = $a"; # ここでは「1234」ではなく「5678」が表示されます
ここでのポイントは、「hashなどの複数の値を持つものでも1つの代表値に置き換えることができる」ということです。
つまり、hashの「値」として使えるわけです。
# hashにhashを入れる my %a; my %a1 = ('key1' => 'aaa', 'key2' => 'bbb'); my %a2 = ('hoge' => 12345, 'fuga' => 6789); # hash %aに%a1と%a2を入れる $a{'a1'} = \%a1; # hashそのものではなく、ポインターを入れる $a{'a2'} = \%a2; # %aの中身を取り出す print "a1->key1 = $a{'a1'}->{'key1'} \n"; # 「aaa」が表示される print "a2->fuga = $a{'a2'}->{'fuga'} \n"; # 「6789」が表示される
もちろん、hashの中身だけでなく、配列もポインタ化する事ができます。
「->」は、「hashのポインターを実体化し、その中の対応するキーの値を取り出す」という意味です。
(hashのデリファレンス演算子といいます。)
これを繰り返せばもっと階層の深い「データベース」を作る事も可能です。
hashのキー一覧を配列で取得する演算子「keys」を使えばキーの一覧を取得できますし、foreachなどに渡せばLoopにも使えます。
ただ、この方法の利用価値があるのは、
- 複数の要素を構造体的に固めて使いたい
- 引数の取れる数が制限されている
- 要素が動的に増えたり減ったりする
- 要素が変更された時にメソッドのインターフェースを変えたくない
などの場合にはメリットがありますが、一方、
- hashの内部構造が実行時まで確定できない
- 階層が深くなるとめんどくさい
- hashの要素がスカラ値なのかリファレンスなのか、何のリファレンスなのか確認する必要がある
など、プログラムを複雑にする可能性もありますので乱用は控えた方がいいでしょう。
ちなみに、
- hashの要素が存在するか確認する
- exists演算子
- hashの要素を削除する
- delete関数
- 値がリファレンスかそうでないか判別する
- ref関数
なども覚えておくと便利です。
履歴
2006/10/23 -- 初版
技術的雑談へ戻る