まだまだたくさんあるよ! Acmeモジュール
ほかにもたくさんあるAcmeモジュールの中から、
モジュール名 | 機能の概要 |
---|---|
Acme::Bleach | 1回目の起動でソースコードを半角スペースとタブの2値に変換して |
Acme::Boom | useするとセグメンテーション違反$SIG{SEGV} を使うことでエラーをトラップして動作確認をしている。ほかにAcme::Segvというモジュールもあるが、 |
Acme::Code::Police | このモジュールがuseされたスクリプト内でuse strict をしていなかったら、 |
Acme::Code::FreedomFighter | 上述のAcme::Code::Policeがuse strict していないファイルを削除しようしたとき、 |
Acme::Damn | blessされたオブジェクトを通常のオブジェクトに戻す。CPANモジュール中、 |
Acme::FizzBuzz | useするだけでFizzBuzz問題 |
Acme::Godot | Samuel Beckettの戯曲 |
Acme::MathProfessor::RandomPrime | 典型的な数学の教授が素数を取り上げる際に採用するアルゴリズムに基づいて、 |
Acme::MetaSyntactic | foo、 |
Acme::MorningMusume | アイドルグループ |
Acme::Math::PerfectChristmasTree | 高さ |
Acme::POE::Tree | コンソールにカラフルなクリスマスツリーが表示される。チカチカ光る |
Acme::PricelessMethods | is_ |
Acme::Python | useするとソースコードが"hiss" という文字列 |
Acme::Sneeze::JP | sneeze |
Acme::Takahashi::Method | ソースコードを1行ごとに画面一杯に表示してくれる |
Acme::Tpyo | 与えた文字列をtypo |
Plack::Middleware::Acme::PHPE9568F 34::D428::11d2::A769::00AA001ACF42 | PHPに実装されている、 |
Plack::Middleware::Acme::Werewolf | 満月の日に狼人間がWebサイトにアクセスできないようにする |
※ Acme::MomoiroCloverパッケージ内にAcme::MomoiroClover::Zが同梱されている
AcmeモジュールからPerlの小技を学ぶ
さて、
Acme::Anythingで学ぶ@INCのフック
Acme::Anythingというモジュールをuseすると、
リスト5で本来ならば存在しないモジュールYasukute OishiiIwashiをuseするとCan't locate Yasukute OishiiIwashi.
というエラーが発生します。しかしこのソースを実行してみると、
use Acme::Anything;
use YasukuteOishiiIwashi; # ← 実在しないモジュール
print "ok, this code didn't die.\n";
$ perl acme_anything.pl
ok, this code didn't die.
@INCにはライブラリまでのパスが入っている
ではこのモジュールはどのようなしくみで動いているのか、
package Acme::Anything;
# 中略
push @main::INC, \&handler_of_last_resort; # (1)
sub handler_of_last_resort {
my $fake_source_code = '1';
open my ($fh), '<', \$fake_source_code; # (2)
return $fh;
};
% perl -E'say $_ for @INC' /home/makamaka/perlbrew/perls/perl-5.14.2/... ..
@INCにコードリファレンスを入れると……
しかし@INCには、perldoc -f require
してみてください。
sub INC {
my ( $self, $module_name ) = @_;
my ( $filename = $module_name . '.pm' ) =~ s{::}{/}g;
open( my $fh, '<', "$YOUR_LIB_PATH/$filename" )
or die "Can't locate $filename in $YOUR_LIB_PATH";
# %INC に何らかの値(通常はファイルまでのパス)を入れる
$INC{ $filename } = "$YOUR_LIB_PATH/$filename";
return $fh;
}
リスト6のAcme::Anythingのコードでもファイルハンドル$fhを返していますが、
@INCを利用したAcmeモジュールたち
このように@INCに細工を仕込むAcmeモジュールを表2にまとめました。現在CPAN上にはほかに6個あります。ネタを仕込むのに好都合な機能であることがうかがえますね。
以上、
モジュール名 | 機能の概要 |
---|---|
Acme::Incorporated | 一定の確率でuseしたモジュールを何もしないモジュールや |
Acme::Module::Authors | プログラムの実行終了時にuseしたモジュールの作者一覧が表示され、 |
Acme::Nothing | モジュールをuseしても何も起こらなくする |
Acme::RemoteINC | useしたモジュールが存在しないときにFTP経由でダウンロード、 |
Acme::Spider | Damian Conwayの作ったモジュールをインストールできなくする |
Acme::use::strict::with::pride | useしたモジュール内にuse strictとuse warningsを加える |
Acmeモジュールから学ぶlvalueサブルーチンとtie変数
戻り値がないことを明示してみる
ご存じのようにPerlには戻り値に対して
sub func_returns_list {
return ('a', 'b', 'c');
}
@array = func_returns_list(); # @array => ('a', 'b', 'c')
$value = func_returns_list(); # $value => 'c'
func_returns_list();
さて、
use Acme::Void;
void context_ware_func();
自分が何をしているのか
lvalueサブルーチン
このvoidは実際のところ何もしない関数なのですが、
void = do_something();
といった使い方もできます。関数voidに向けて値を返すのですが、
リスト9
my $value;
sub func_returns_lvalue : lvalue { # (1)
$value; # (2)returnは付けない
}
func_returns_lvalue() = 3; # $value = 3
Acme::Lvalue
lvalueサブルーチンを利用した、
リスト10を実行すると、
use v5.16; # 5.16.0 以降が必要
use Acme::Lvalue qw(:builtins);
my $x;
say sqrt(9); # => 3
sqrt($x) = 3;
say $x; # => 9
say reverse( 'abcde' ); # => edcba
reverse( $x ) = 'edcba';
say $x; # => abcde
どんなしくみ?
Acme::Lvalueはsqrtやreverseなどの組込み関数と同名のサブルーチンをエクスポートします。そのサブルーチン内では渡された引数をプロキシクラスにtieして返します。すると関数が右辺値として評価されるときはtieクラスのFETCHが呼び出されるのでこのときは本来の組込み関数と同じ処理を行います。逆にlvalueのときはtieクラスのSTOREが呼び出されるので、perldoc perltie
をご覧ください)。
変数をクラスにtieする
変数
リスト11
use Proxy;
sub lval_func : lvalue { # (1)
# 変数をProxy クラスにtie します。
# 引数として本来の処理と、lvalue 時の処理を渡します。
tie my $proxy, 'Proxy', \$_[0],
sub { sqrt $_[0] }, sub { $_[0] * $_[0] }; # (2)
$proxy; # (3)
}
say lval_func(9); # (4)
my $x;
lval_func($x) = 3; # (5)
say $x;
$_[0] = $x
)
Acme::Lvalueの簡略コード
それではAcme::Lvalueのソースを単純化したコードをもとに、
use strict;
use warnings;
package Proxy; # (1)
sub TIESCALAR { # (2)
my ( $class, $value, $func, $lval_func ) = @_;
my $self = {
# lval_func に渡された値を保持
value => $value,
func => $func, # 本来の処理
# lvalue として呼ばれたときの処理
lval_func => $lval_func,
};
bless $self, $class; # (3)
}
sub FETCH { # (4)
my ( $self ) = @_;
# 右辺値として呼ばれたとき呼ばれる
$self->{ func }->( ${$self->{ value }} ); # (5)
}
sub STORE { # (6)
my ( $self, $value ) = @_;
# 左辺値として呼ばれたとき呼ばれる
my $lvalue = $self->{ value };
$$lvalue = $self->{ lval_func }->( $value ); # (7)
}
TIESCALAR
リスト12
FETCH
リスト12
STORE
リスト12
右辺値として呼び出す
リスト11sub { sqrt $_[0] }
が実行されます。
左辺値として呼び出す
リスト11sub { $_[0] * $_[0] }
が実行されます。リスト12
tie my $proxy, 'Proxy', \$_[0],
で渡した$_[0]に格納されます。この$_[0]は元は$xですので、つまるところ$xに結果が入ることになります。
以上、Acme::Void、Acme::LValueからかなり強引にlvalueとtie変数を学ぶことができました! 苦しかったですね……。なお、lvalueサブルーチンは実験的な機能で将来のPerlのバージョンでは実装が変わる可能性があることにご注意ください。現実問題として、lvalue機能が実際に必要な機会はそうないと思います。詳細はperldoc perlsubでご確認ください。
まとめ
Acmeモジュールに魅了された人たちによって、さまざまなところでAcmeモジュールが紹介されています。一例を挙げるとPerl Advent Calendar Japanには過去2回Acme専用のトラックが開催されています[4]。また筆者にAcmeの由来をご教示くださった冨田さんによる紹介などなど……挙げていくとキリがありません。
仕事や勉強の合間にさまざまなAcmeモジュールと戯れると、一服の清涼剤になると思います。また、Acmeモジュールのおもしろ動作に直面して「どのようなしくみだろう?」と興味を持つこともあるでしょう。そこでソースコードを見ることで、Perlに対する新たな発見があるかもしれません。初めてCPANにアップしたモジュールがAcmeモジュールだった人もいらっしゃることでしょう。
Acmeというカテゴリが作られたおかげで私たちは現在、モジュール名で揉めることもなく安心して ジョークモジュールをリリース&インストールして楽しむことができます。先人の深い叡智(えいち)と、AcmeモジュールをCPANにアップしてくれる作者の方々に深く感謝したいと思います。
さて、次回の執筆者は近藤嘉雪さんで、テーマは「リファレンス入門」です。