Perl Hackers Hub

第19回Acmeで広がるPerlの世界―CPANは愉快なジョークモジュールの宝庫(1)

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回は同人誌『Acme大全』の発行者として知られるまかまか般若波羅蜜(はんにゃはらみつ)さんで、テーマはAcmeモジュールです。

Perlの徒花(あだばな)Acmeモジュール

Perlでいつもお世話になるCPANモジュールですが、中には役に立たないジョーク系のモジュールもあります。そのようなモジュールには慣習的に「Acme」という名前が付けられていて、現在CPAN上には400個以上のディストリビューションが登録されています。

もともとCPANには特に名前付けのルールもなくジョークモジュールがリリースされていました。しかしそのようなモジュールが多くなるにつれ、何とかしようということでPerlコミュニティの重鎮Damian ConwayによってAcmeという名前空間が提唱されました。この名前の由来は、アメリカのアニメ「バッグス・バニー・ショー」で欠陥商品を送りつけてくる通販会社Acmeからきています

たとえばよく知られているAcmeモジュールの一つに、Acme::EyeDrops[1]があります。これは、コードを「見栄えの良いもの」に変換するモジュールです。

AcmeモジュールをはじめとしてCPANモジュールのインストールにはcpanmがお勧めです。コマンドラインから、

$ curl -L http://cpanmin.us | perl - App:cpanminus
$ cpanm Acme::EyeDrops

などでインストールします。

次に、リスト1のような簡単なスクリプトを作成します。(1)でShapeにコマンドラインからの引数を渡しています。Shapeに指定した値がコードを変換する際の形を決定します。(2)でSourceStringに'print "Hello World\n"'と指定しています。これが変換対象となるコードです。

そして、コマンドラインから引数としてcoffee(数ある変換形のうちの一つ)を渡して実行してみます図1⁠。出力結果は、誰がどう見てもcoffee(コーヒーカップ)です。

リスト1 Acme::EyeDropsを使ったスクリプト
use strict;
use warnings;
use Acme::EyeDrops qw(sightly);
my $type = $ARGV[0] || 'beer';
print sightly( {
    Shape => $type, # (1)
    SourceString => 'print "Hello World\n"', # (2)
    Regex => 1,
} );
図1 Acme::EyeDropsを使ったスクリプトの実行結果
$ perl eyedrops.pl coffee > coffee.pl
1 shapes completed.
$ cat coffee.pl
''=~('('.'?'.'{'.('`'|'%').('['^'-').('`'|  '!').(
'`'|',').'"'.('['^'+').('['^')').('`'|')') .('`'|'.'
).('['^'/').('{'^'[').'\\'.'"'.('`'^'(').('`'    |((
'%'))).('`'|',').('`'|',').('`'|'/').("\{"^      '['
).('{'^',').('`'|'/').('['^')').('`'|',').       (((
 '`'))|'$').'\\'.'\\'.('`'|'.').'\\'.'"'.       '"'
 .'}'.')');$:='.'^'~';$~='@'|'(';$^="\)"^     '[';
  $/='`'|'.';$,='('^'}';$\='`'|('!');$:=    ')'^
   '}';$~='*'|'`';$^='+'^'_';$/='&'|'@'   ;$,=
    '['&'~';$\=','^'|';$:='.'^'~';$~=('@')|
     '(';$^=')'^'[';$/='`'|'.';$,='('^'}'
      ;$\='`'|'!';$:=')'^'}';$~='*'|
       '`';$^='+'^'_';$/=('&')|
        '@';$,='['&'~';#;#

もちろん実行すれば期待どおりの動作をします。

$ perl coffee.pl
Hello World

このように、一見何の役に立つかわからないけれども、やはり実際役に立たないようなモジュールに代表されるのがAcmeモジュールです。筆者は年に一度『Acme大全』という同人誌を発行していて、CPANに登録されている全Acmeモジュールを紹介しています。そのようなこともあって、Acmeモジュールに対しては並々ならぬ思い入れがありますので、今回はみなさんにAcmeモジュールのすばらしさをお伝えできればと思います。

なお本稿の実行環境は、特に断りがない限りPerl 5.14.2を使用しています。

さまざまなジョークモジュール

ここでは、初めて実行したときに「これはしてやられた(笑⁠⁠」と筆者が感じたAcmeモジュールを紹介します。

Acme::Tests─⁠─テストする側からテストされる側に

通常、CPANモジュールをインストールするときには同梱のテストを実行すると思います。しかし、このモジュールでは逆にテストが出題されます。

図2のように次々と問題が出題され、全問正解しないとテストにパスできません。

図2 Acme::Testsの実行
$ cpanm --look Acme::Tests
Acme-Tests-0.03.tar.gz が展開されてディレクトリを移動します
$ perl Makefile.PL
$ make test
Who plays on slashdot.org ?
  (1) Cowboy Neal
  (1) Cowboy Neal
  (1) Cowboy Neal
  (1) Cowboy Neal
==> 1
Who Invents Perl ?
  (1) Larry Nelson
  (2) Larry Wall
  (3) Larry King
  (4) Some guy with "Perl" in his name
==> 2
2 + 2 = ?
==> 4
Who writes Acme.pm ?
  (1) acme
  (2) spoon
  (3) ingy
  (4) all of them
==> 1
Verifing your answer...
t/00.simple.t .. 1/?
# Failed test at t/00.simple.t line 18.
# Looks like you failed 1 test of 4.
t/00.simple.t .. Dubious, test returned 1 (wstat 256,
0x100)
Failed 1/4 subtests

テストを自作してみる

すばらしいことに、Acme::Testsを使えば自作のテスト出題モジュールも簡単に作れます。

Acme::Testsをuseして、__DATA__行以降に問題文、選択肢、答え、区切り文字----を加えていくだけですリスト2⁠。

リスト2 自作のテスト出題モジュール(本体)
package MyTests;
use strict;
use Acme::Tests -Base;
our $VERSION = '1.00';

__DATA__
コミックマーケット84 の3 日目は何曜日?
(1) 金曜日
(2) 土曜日
(3) 日曜日
(4) 月曜日
Ans: 4
----

今回、起動スクリプトはAcme::Testsのテストファイルディレクトリに付属のものを使いまわしましたリスト3⁠。ユーザの解答を標準入力から取得し、答えの番号と突き合わせています。proveコマンドで確認してみましょう。

リスト3 自作のテスト出題モジュール(起動スクリプト)
use strict;
use MyTests;
use Test::More;

my $t = MyTests->new;
my (@questions, @answers);

while (my $q = $t->next_question) {
    print STDERR "\b\n$q\n==> ";
    my $ans = <>;
    push @questions,$q;
    push @answers,$ans;
}

print STDERR " 結果は...\n";
for (0..$#questions) {
    ok( $t->is_correct($questions[$_],$answers[$_]) )
}

done_testing;
$ prove t/mytest.t
コミックマーケット84 の3 日目は何曜日?
(1) 金曜日
(2) 土曜日
(3) 日曜日
(4) 月曜日
==>

ちゃんとテストが出題されました。とても楽しいですね!

Acme::Addslashes─⁠─ PHPのあの関数を導入してみる

ところで、PHPにはaddslashesという組込み関数があります。addslashesは'(シングルクォート⁠⁠、"(ダブルクォート⁠⁠、\(バックスラッシュ⁠⁠、NULLの前にエスケープのためにスラッシュを付ける関数です。Perlにはこのような関数はありませんが、Acme::Addslashesを導入すれば「似たようなもの」が手に入ります。

リスト4を実行すると、図3のような結果になりました。なんだかやたらめったらスラッシュが加えられていて、とてもセキュウアァァ……な気がしますね[2]⁠。

リスト4 Perlでaddslases?
use 5.012;
use strict;
use Acme::Addslashes qw(addslashes);

my $unsafe_str = "evil code";
my $slashed_str = addslashes( $unsafe_str );

say $slashed_str;

$ perl addslases.pl
図3 Acme::Addslashesの結果
図3 Acme::Addslashesの結果

おすすめ記事

記事・ニュース一覧