IBM®
本文へジャンプ
    Japan [変更]    ご利用条件
 
 
検索範囲検索:    
    ホーム    製品    サービス & ソリューション    サポート & ダウンロード    マイアカウント    
skip to main content

developerWorks Japan  >  Linux | Open source  >

洗練されたPerl: JAPHのすばらしさ

Just Another Perl Hacker(JAPH)

developerWorks
ページオプション

JavaScript を要するドキュメントオプションは表示されません

原文はこちら

原文はこちら


レベル: 初級

Teodor Zlatanov (tzz@iglou.com)Gold Software Systems

2001年 7月 01日

Perlカルチャーの中心とも言えるJAPHは、「Just another Perl hacker」という出力を行う短いスクリプトのことを指します。この記事は、初級、中級のPerlプログラマーを対象として書かれていますが、ここでは経験豊富なPerlファンさえも驚かせ、魅惑してしまうようなJAPHの例をいくつか紹介しています。この記事の著者Teodor Zlatanov氏は、1992年以来この分野に取り組み、特にテキスト構文解析のオープン・ソース化を専門とするPerlのエキスパートとして活躍しています。

JAPHは基本的に、USENET署名に収まるよう4行以下のコード(1行あたり80文字以内)で文字列「Just another Perl hacker」を生成するものです。USENET署名はPerlよりもはるかに古いものですが、JAPHは、USENET署名という長年にわたる伝統とPerlの優れた技術という奇妙な組み合わせを生み出しています。

JAPH:「Just another Perl hacker」

私たちの知るところでは、JAPHフォーマットは、1990年代初頭にRandal Schwartz氏によって広められました(いくつかの情報筋の意見が一致しているところです)。JAPHは、comp.lang.perl.miscニュースグループのAbigail氏のように、このジャンルにおける精力的な達人によって生み出され、今日でも作られたときのままの状態で人々に知られています。

以下の説明では、初心者から中級までのPerlプログラマーに適している、CPAN(参考文献を参照)にある基準的なリストについて検討してみましょう。ここでは、さまざまなテクニックに関して簡単に解説しますが、興味のある方は、Programming Perl、3rd Edition(参考文献を参照)に詳細な解説がありますので、それをご参照ください。

ここで示されている例を正しく理解するためには、システムにPerl 5.6.0をインストールする必要があります。できれば、最近(2000年以降)のUNIXシステムのメインストリーム(Linux、Solaris、BSD)であることが望まれます。ここにあげる例は、PerlやUNIXの以前のバージョン、また他のオペレーティング・システムでも再現できますが、うまくいかない場合には、練習として皆さん自身で解決してみてください。JAPHはすべて、標準リストの形式(日付と作者の属性がある)で示されています。




上に戻る


優しいJAPH

難しい問題に取りかかる前に、JAPHの草分け時代に戻ってRandal Schwartz氏によって作成された、面白くて簡単な4つのコードを見てみることにしましょう。この最初の例を見ると、すべてのJAPHが必ずしも分かりにくいものであるとは限らず、中には簡単なものもあるということが分かります。


リスト1. 簡単なtrue条件
                
Date:         18 Jun 90 15:53:11 GMT
From:         merlyn@iwarp.intel.com (Randal Schwartz)
print "Just another Perl hacker," if "you can't think of anything better..."

リスト1では、ストリングが空ではないためif()ステートメントが常に真と評価します(空ストリング""、"0" ストリング、数値0またはその等価、あるいはundefの場合のみ偽と評価)。このようにして、printステートメントが常に評価されます。


リスト2. printfの使用
                
Date:         15 Jun 90 22:06:24 GMT
From:         merlyn@iwarp.intel.com (Randal Schwartz)
printf "%s %s %s %s%c", 'Just', 'another', 'Perl', 'hacker', 44

リスト2は、もうひとつの初期の見本といえるもので、出力の生成にprintf()関数を使い、PerlがCのような体裁を取ることができることを示しています。

リスト3は、少し分かりづらくなっています。ここでは、並べ替えたアレイをprint()に与え、語間にスペースを入れて出力します($,は、すべてを同時に出力する場合にアレイ要素の間に何を置くかをPerlに指示する変数)。


リスト3. アレイの並べ替え
                
Date:         5 Jun 90 19:07:58 GMT
From:         merlyn@iwarp.intel.com (Randal Schwartz)
$,=" "; print +("hacker,","Just","Perl","another")[1,3,2,0];

アレイの前にある + は、カッコに関数呼び出しの意味を持たせるのではなく、その後に続くものをひとまとめのパラメーターとして(カッコがあるので、この場合にはアレイ)print()に処理させるためのものです。つまり、 print ('a', 'b')[1] の場合、print()は 'a' と 'b' を出力すべき第1、第2のパラメーターとみなしますが、[1]をどうすればよいかが分からなくなるため、こうした状況を避けるようにしています。

リスト4は、ファイルにある最も古いJAPHのひとつであり、split()sort()grep()を使う、やや手の込んだものです。


リスト4. sortの後にgrepを行う
                
Date:         6 Feb 90 22:31:17 GMT
From:         merlyn@iwarp.intel.com (Randal Schwartz)
print grep(s/^\d+(.*)/$1 /, sort(split(/ /,"8hacker, 4Perl 1Just 2another")));

最初に、開始ストリングを「8hacker」、「4Perl」、「1Just」、「2another」という4つの要素に分割します。
次に、ソートして(デフォルト設定では英数字順)「1Just」、「2another」、「4Perl」、「8hacker」を生成します。

「10Just」だったとしても「8hacker」の前にくることに注意してください --  これは数字のソートではありません。

ソートされたリストはgrep()に渡されます。これによって最初の桁の数字を取り除き、残った文字にスペースを付加します。結果は、「Just」、「another」、「Perl」、「hacker」となります。

そして最後に、print()がそのリストに呼び出され、要素ごとに出力が行われます。




上に戻る


意地悪なJAPH

よい話はここまでにして、今度は「根っから悪い」JAPHを見てみましょう。よいJAPHは優しく教えてくれますが、この「意地悪なJAPH」は皆さんの脳ミソをプレッツェルのようにねじ曲げてしまいます。10分間見つめて頭痛しか起きない場合、そのJAPHは「意地悪なJAPH」であるということです。


リスト5. 置換とeval
                
Date:         26 Mar 90 16:20:37 GMT
From:         raymond@sunkist.berkeley.edu (Raymond Chen)
$_='x"Not ";"x\"another \";\'x\\"perl \\";x\\"hacker,\\"\'"';s/x/print/g;eval eval eval;

ここで示されているのは、ひとつの語を別の語に置換してから出力の評価を行う一般的なテクニックです(後にPerlコードの作成を行います)。この例では、すべての「x」「print」と置き換えます。また、Perlの引用符規則にも注意してください。評価後、Perlはそのストリングを、x"Not ";"x\"another \";'x\"perl \";x\"hacker,\"'"と認識します(print()をs///コマンドの前に置くと表示できます)。

ストリングは単一引用符で始め、単一引用符で終わらせます。単一引用符の先に目を向けると、2つが(バック・スラッシュで)エスケープされ、3つめが実際の単一引用符であることがわかります。

ここで、置き換えを実行します(結果を確認するには、s///コマンドの後にprint()を加えます)。print"Not ";"print\"another \";'print\"perl \";print\"hacker,\"'"

さて、本題のコマンドに移りましょう。なぜ、ここでeval()を1回だけでなく、3回も実行するのでしょうか。注意して見てください。2番目のprint()はストリングの内側にあるため、最初のeval()によっては評価されません。しかし、最初のeval()は、評価された第1レベルのストリングprint"another ";'print"perl ";print"hacker,"'を返します。そして「Not」を出力します。なぜ、ストリングの最初の部分が、最初のeval()によって返されないのでしょうか。それは、eval()は、最後に評価したもののみを返すからです。オペレーション中にこれを確認する場合は、最後のステートメントを「eval eval eval」ではなく、「print eval」とします。

2番目のevalは何を行うためのものでしょうか。これは2番目のprint()ステートメントのみを評価し、3番目、4番目のprint()ステートメントは評価しません。よく見ると、両方ともひとつの単一引用符で囲まれたストリングであることがわかります。2番目のevalは、3番目と4番目のprint()ステートメントを含むストリングを返し、「another」を出力したprint()ステートメントを除去します。そのため、2番目のevalは、print"perl ";print"hacker,"を返します。

そして、3番目のevalはこれらの2つのprint()ステートメントを返して、JAPHを完了(「Not another perl hacker」を出力)します。

このように、意地悪なJAPHの場合、その分析には時間がかかります。ここで解読したような簡単なものであっても、分析してみるとそれなりに複雑であることが分かります。

では、根っから意地悪なJAPHの例をもうひとつ見てみましょう。


リスト6. Abigail氏の意地悪な問題
                
#Abigail
$_ = "\x3C\x3C\x45\x4F\x54"; s/<<EOT/<<EOT/e; print;
Just another Perl Hacker
EOT

リスト6も、一見して簡単なJAPHのように見えます。なぜでしょう。ストリングがそのままあるからです。ではどこに問題があるのでしょう。そうですね、Abigail氏は、これまで行ってきたことを何か別の方法で行うというスタイルを多くとります。ここでは、たとえばs///オペレーターに「e」という修飾子が与えられています。これによって、置換を行う前に右側の式の評価が行われます。したがって、「<<EOT」は、次の行からEOTの前まで、つまりこの場合は「Just another Perl Hacker」と置き換えられます。

コード化された$_ストリングは「<<EOT」を持つようになるため、s///オペレーターによって行なわれる置換によってストリング「<<EOT」は「<<EOT」の評価結果、すなわちストリング「Just another Perl Hacker」によって置換されます。そして、print()ステートメントによってこのストリングの出力が行われます。

コード化されたストリングと、一見シンプルな置き換えは、JAPHの中心です。特に置き換えは、驚くほどユニークな方法で行われ、その中には皆さん自身のコーディングに役立つものがあるかもしれません。

Abigail氏による悪魔の難問をもうひとつ紹介しましょう。


リスト7. Abigail氏の示すプロトタイプ
                
#Abigail
perl -wle 'sub _ "Just another Perl Hacker"; print prototype \&_'

このJAPHを理解するには、プロトタイプに関する実際の知識が必要です。ここで行われていることを理解するために、「perldoc -f sub」と「perldoc -f prototype」のドキュメンテーションをご覧ください。基本的に、"_"の本文ではなく、「Just another Perl Hacker」のプロトタイプによって新しい関数が作られています。

Programming Perl, 3rd Edition(参考文献を参照)でプロトタイプに関するセクションをご覧になれば、プロトタイプがストリングにはなりえないことがわかります。Abigail氏はこの事実を都合よく無視して(この関数が決して使用されないので)、その無効なプロトタイプを出力しています。

このコードによってプログラムが破壊されるようなことはおそらくありませんが、それだからといって、このような事が許されるのでしょうか。これはまともな事とは言いがたいのでは?やはり、問題がまったくないとは言えないようです。驚くようなものが数多く出回っていますが、少なくともこの例は、関数定義においてプロトタイプの使用の適性がチェックされていないことを示すものです。またこれは、"_"で呼び出した関数定義が可能であるということも示しています(組み込みの"_"オペレーターと競合する可能性があるため、通常は行いません)。




上に戻る


醜いJAPH

優しいJAPHと意地悪なJAPHの両方を見ました。あと残っているのは「醜いJAPH」です。これは、文字通り「醜い」もので、いろいろと手の込んだ方法で私たちにショックを与え、たちまち私たちは頭痛薬なしではいられない状態に陥ってしまいます。

皆さんの大切なお友達(かわいいペット君は残念ながら無理でしょう)へのプレゼントとしてうってつけのKickstart氏の例を紹介します。これが「醜いJAPH」である理由は1つだけではありません。4行以内という制限は完全に忘れ去られています。


リスト8. 燃えるハート
                
#Kickstart from http://www.perlmonks.com/
#note: a slight valentine variation :)
      $LOVE=               AMOUR.
    true.cards.        ecstacy.crush .hon.promise.de    .votion.partners.
 tender.truelovers. treasure.affection.
devotion.care.woo.baby.ardor.romancing.
enthusiasm.fealty.fondness.turtledoves.
lovers.sentiment.worship.sweetling.pure
attachment.flowers.roses.promise.poem;
 $LOVE=~ s/AMOUR/adore/g; @a=split(//,
  $LOVE); $o.= chr (ord($a[1])+6). chr
   (ord($a[3])+3). $a[16]. $a[5]. chr
    (32). $a[0]. $a[(26+2)]. $a[27].
      $a[5].$a[25]. $a[8].$a[3].chr
        (32).$a[29]. $a[8].$a[3].
          $a[62].chr(32).$a[62].
           $a[2].$a[38].$a[4].
               $a[3].'.';
                 print
                  $o;

信じようと信じまいとこれはちゃんと動きます。でもいったいこれは何をしているのでしょう?

変数$LOVEが、この謎を解く第一のカギです。$LOVE変数が何であるかを知るために、まずスクリプトを分解して実行可能ファイルに置き、デバッグができるようにします。

出力は(改行なし)次のようなものです。$LOVEは、AMOURtruecardsecstacycrushhonpromisedevotionpartnerstendertrueloverstreasureaffection
devotioncarewoobabyardorromancingenthusiasmfealtyfondnessturtledovesloverssentiment
worshipsweetlingpureattachmentflowersrosespromisepoem
"
Perlは、この語をすべてそのままストリングとして解釈します。


リスト9. 愛を捨てる
                
#!/usr/bin/perl
use Data::Dumper;
      $LOVE=               AMOUR.
    true.cards.        ecstacy.crush .hon.promise.de    .votion.partners.
 tender.truelovers. treasure.affection.
devotion.care.woo.baby.ardor.romancing.
enthusiasm.fealty.fondness.turtledoves.
lovers.sentiment.worship.sweetling.pure
attachment.flowers.roses.promise.poem;
print Dumper $LOVE;
 $LOVE=~ s/AMOUR/adore/g; @a=split(//,
  $LOVE); $o.= chr (ord($a[1])+6). chr
   (ord($a[3])+3). $a[16]. $a[5]. chr
    (32). $a[0]. $a[(26+2)]. $a[27].
      $a[5].$a[25]. $a[8].$a[3].chr
        (32).$a[29]. $a[8].$a[3].
          $a[62].chr(32).$a[62].
           $a[2].$a[38].$a[4].
               $a[3].'.';
                 print
                  $o;

ここで、「AMOUR」を「adore」に置き換え、次に$LOVEを@aと呼ばれる個々の文字の配列に分割します。@aの最初の要素は「a」、2番目の要素は「d」、というように続き、次のような構成になります。adoretruecards
ecstacycrushhonpromisedevotionpartnerstendertrueloverstreasureaffectiondevotion
carewoobabyardorromancingenthusiasmfealtyfondnessturtledovesloverssentimentworship
sweetlingpureattachmentflowersrosespromisepoem
"

最後に、@a配列から文字を選び、ストリング$oを構成します。時として、形を変えることが必要な場合もあります(ただ物事を面白くするために)が、結局、愛は勝つのです。

分解されたスクリプトは次のようになります。


リスト10. 分かたれた愛
                
#!/usr/bin/perl
$LOVE = "AMOURtruecardsecstacycrushhonpromisedevotionpartners".
        "tendertrueloverstreasureaffectiondevotioncarewoobaby".
        "ardorromancingenthusiasmfealtyfondnessturtledoveslovers".
        "sentimentworshipsweetlingpureattachmentflowersroses".
        "promisepoem";
$LOVE=~ s/AMOUR/adore/g;
@a=split(//, $LOVE);
$o.= chr (ord($a[1])+6). chr (ord($a[3])+3). $a[16]. $a[5] .
#           j                   u              s       t
 chr (32). $a[0]. $a[(26+2)]. $a[27]. $a[5].$a[25]. $a[8].
# space     a          n        o        t    h       e
 $a[3].chr (32).$a[29]. $a[8].$a[3]. $a[62].chr(32).$a[62].
#  r   space      p       e     r      l    space     l
 $a[2].$a[38].$a[4]. $a[3].'.';
#  o     v      e      r
print $o;

まさに「ラブ・イズ・ア・ミステリー」です。




上に戻る


自分でJAPHを試してみる

数々の面白いミニ・スクリプトを取り上げ、さまざまな興味深いPerlテクニックを見てきました。JAPHは難しいクイズとして作られているものですから、説明を聞いてしまうよりも、自分自身でその構造を解き明かしてみる方がはるかに面白いでしょう。しかし、知っておかなければならないのは、JAPHの多くがPerlに関する高度な知識、そして忍耐を必要とするということです。JAPHはまだ他にもあります。是非、怖がらずに参考文献セクションにあるその他のJAPHに自らチャレンジしてみてください。

安心してください。JAPHの解読は経験を積むことによって段々と簡単になっていくでしょう。16進法によるストリングのエンコードや$の変更など、簡単なトリックによって変数を見抜くことも簡単に(それでもじっくり取り組むことは必要ですが)できるようになります。

JAPHの作成に時間と努力を惜しまないすべての皆さんに感謝します。すべてのJAPHは、常に何か新しいものを教えてくれるので、私は新しいJAPHをいつも楽しみにしています。私の知る限り、このように「簡潔であって分かりにくい」形式の言語は他にありません(そして、これが誇るべきものか恥ずかしいものかも誰も分からないままに)。しかし、他の多くの言語にもこのような「簡潔であって分かりにくい」例題があります。関心のある方は、参考文献をご覧ください。C言語のものは特に注目に値します。



参考文献



著者について

Teodor Zlatanovは1999年にボストン大学を卒業し、コンピューター・エンジニアリングで学位を取得しています。1992年以来プログラマーとして働いており、Perl、Java、C、C++などの言語を使用してきています。関心を持っている領域としてはオープン・ソース作業、Perl、テキスト構文解析、3層のクライアント/サーバー・データベース・アーキテクチャー、Unixのシステム管理などです。助言や間違いの指摘を歓迎しています。連絡先はtzz@bu.eduです。




記事の評価


サイト改善のため、ご意見をお寄せください。こちらのフォームからお願いいたします。



 


 


不充分・不完全である大変素晴らしい
 


この記事を共有する

del.icio.us del.icio.us newsing newsing FC2ブックマーク FC2ブックマーク
Choix! Choix! ニフティクリップ ニフティクリップ Yahoo!ブックマーク Yahoo!ブックマーク
MM/memo MM/memo CZブックマーク CZブックマーク livedoorクリップ livedoorクリップ
はてなブックマーク はてなブックマーク Buzzurl(バザール) Buzzurl(バザール)




上に戻る


    日本IBMについて プライバシー お問い合わせ