2008年07月05日 01:30 [Edit]

perl - tie()って何をtieするの?

camel

私はタイをまとうのがだいっきらいですが、Perl 5の中で一番好きな関数がtie()だったりします。

tie関数とは - 燈明日記
すると、裏で処理が動き、オブジェクトをコンストラクトしたり、とある処理結果が参照できたり、代入したものに対応したものを格納出来たりする。

というわけで、tie()について語っておくことにしましょう。


tie()とはなにか?

ずばり、「オブジェクトではない、perlの組み込みデータタイプを裏でオブジェクト化する仕組み」のことです。それがなぜtieと呼ばれるかと言えば、変数を指定されたクラスに結びつけるから、です。

論より証拠、実際に見てみましょう。

use strict;
use warnings;
{
    package MyScalar;
    use Carp;
    sub TIESCALAR {
        my $pkg = shift;
        carp "TIESCALAR('$pkg')";
        bless \do { my $scalar }, $pkg;
    }
    sub FETCH($) {
        my $this = shift;
        carp "FETCH($this)";
        return $$this;
    }
    sub STORE($$) {
        my ( $this, $value ) = @_;
        carp "STORE($this, $value)";
        $$this = $value;
    }
    sub DESTROY {
        my $this = shift;
        carp "DESTROY($this)";
    }
}

{
    tie my $scalar, 'MyScalar';
    $scalar = 1;
    print $scalar, "\n";
    $scalar++;
    print $scalar, "\n";
    print 'tied($scalar) = ', tied($scalar), "\n";
    tied($scalar)->STORE(3);
    print $scalar, "\n";
}

CodePadで動かしたいので、ここでは同一ファイル上にMyScalarパッケージを実装しています....と思って今CodePadを見たらメンテ中[復活したのでリンク追加]。なので以上は各自実行してください。こんな感じになったと思います。

[Run via codepad]
TIESCALAR('MyScalar') at tiescalar.pl line 28
STORE(MyScalar=SCALAR(0x181c290), 1) at tiescalar.pl line 29
FETCH(MyScalar=SCALAR(0x181c290)) at tiescalar.pl line 30
1
FETCH(MyScalar=SCALAR(0x181c290)) at tiescalar.pl line 31
STORE(MyScalar=SCALAR(0x181c290), 2) at tiescalar.pl line 31
FETCH(MyScalar=SCALAR(0x181c290)) at tiescalar.pl line 32
2
tied($scalar) = MyScalar=SCALAR(0x181c290)
STORE(MyScalar=SCALAR(0x181c290), 3) at tiescalar.pl line 34
FETCH(MyScalar=SCALAR(0x181c290)) at tiescalar.pl line 35
3
DESTROY(MyScalar=SCALAR(0x181c290)) at tiescalar.pl line 0

このことから、以下のことがわかります。

  • tie my $scalar, 'MyScalar'は、TIESCALAR('MyScalar')を実行する
  • $scalarの値にアクセスすると、MyScalar::FETCH()が実行される
    • tied($scalar)->FETCH()と同等
  • $scalarに代入すると、MyScalar::STORE()が実行される
    • $scalar = $valueが、tied($scalar)->STORE($value)と同等
  • スコープを抜けたところで、MyScalar::DESTROY()が実行される

要するに、tie()された変数に対する操作が、オブジェクトに対するメソッド呼び出しに変換されるのです。

Scalarの場合、上で見た通りなかなかシンプルですが、これがArrayやHashやFile Handleとなると、もう少し実装せねばならないメソッドが増えます。が、考え方は同じです。また、Perl 5にはTie::Scalar, Tie::Array, Tie::Hash, そして Tie::Handleというベースクラスがすでに用意されているので、これらを継承すれば必要なメソッドだけを自作して楽をすることも出来ます。

ところが、Damian先生はこのtieがお嫌いなのです。

"Perl Best Practices" P.451

Don't tie variables or filehandles.
変数もファイルハンドルもtie()するな

確かに、Perl 5のオブジェクト指向を知っていれば、わざわざtie()を介さずとも直にオブジェクトを使えばいいわけです。実際その方が高速でもあります。

しかし、tie()があるおかげで、Perl 4しか知らない人でもPerl 5のオブジェクトのパワーを利用することが出来ますし、Perl 5には組み込まれていない機能を「目立たない」ように実装することも可能になっています。

そもそもそういうDCONWAY先生自身、tieを使うモジュールをいくつもつくってます。 Readonlyなんかその典型です。TieぎらいのDCONWAY先生がTieでReadonlyを実装して、Tieに頼らないAttribute::Constantをtie好きの私が実装するなんてなんたる皮肉でしょうかねえ。

tieは、OOも知らないプログラミング初心者にとってUseすべきものであるのと同時に、OOを習うものには(perlに限らず!)恰好の教材です。独自のTieモジュールを実装できれば、Perl 5のOOはほぼ免許皆伝といってもよいぐらい。

みなさんも、もっとtieを活用してみましょう。Perl以外には滅多にない機能ですし、ほんと、気持ちいいですよ。もちろん使いすぎれば自縄自縛にはなりますが。

Dan the Perl Monger

See Also:

この記事へのトラックバックURL

この記事へのコメント
トーリスー・ガーリさん、
ありゃま、ほんとだ。直しました。
Dan the Typo Generator
Posted by at 2008年07月06日 02:16
これをみてprelはもうダメだなとあらためて思いました。
ワンライナーでperl4を使う以上の事は、Pythonのほうがいい。
Posted by 苺 at 2008年07月05日 13:54
誤:$scalarに代入すると、MyScalar::FETCH()が実行される
正:$scalarに代入すると、MyScalar::STORE()が実行される

愛読者です。typo しか指摘できなくてスミマセン。
Posted by トーリス・ガーリ at 2008年07月05日 08:31