ROUTE 3390

備忘録的な用途で書いていますが、どなたかの役に立つ事があれば嬉しいです。

いまさら素数戦争のdankogai.plを読んでみた

2012年のYAPC::Asiaで紹介されていた素数戦争

「10000番目までの素数の和を求める」
というお題をいかに早く算出するか!というものでした。

私も参加ダメダメながら参加して楽しんでいました。私の記録はこちら

そこで気になっていたのが「殿堂」となっていたdankogai.pl

length q cmp lc and print chr oct oct ord qw q do q and print chr oct oct ord q mkdir m and print chr oct oct ord qw q for q and print chr oct oct oct ord q eq le and print chr oct ord uc qw q bind q and print chr oct ord uc q each ne and print chr oct oct ord qw q dump q and print chr oct oct oct ord q eq ne and print chr oct oct oct ord q eq ne

なんじゃこりゃ?本当にPerl? って感じですよね。
でも実行すると確かに10000番目までの素数の和である496165411が表示されます!!

当時は感心だけして、どうなっているのかまで関心は沸いていませんでした。
(漠然とすごいなー danさんってすごいんだなーって)


あれから半年、、
いまさらちゃんと読んでみたいと思います。


一番左からじっくり見ていってもいいですが、パッと目につくのはprintでしょうか。
そして、printに注目すると、必ずその前にandがあることが分かります。

length q cmp lc and print chr oct oct ord qw q do q and print chr oct oct ord q mkdir m and print chr oct oct ord qw q for q and print chr oct oct oct ord q eq le and print chr oct ord uc qw q bind q and print chr oct ord uc q each ne and print chr oct oct ord qw q dump q and print chr oct oct oct ord q eq ne and print chr oct oct oct ord q eq ne


andというのは&&と同じです。
&&は下記のようにifとかで使うやつです。

my $sasa = 1;
my $kure = 1;
if ($sasa && $kure ) {
    print 'sasaとkureがどちらもtrueと評価されるから表示される';
}
#こんな使い方も出来る
$kure && print '&&の左辺がtrueならこのprint文も評価される';
$kure || print '&&の左辺がtrueならこのprint文は評価されない';

dankogai.plではandを使う事で、本来なら;(セミコロン)で区切るような
処理を1行でやってしまっているんですね。


ちょっと読み易いようにandを削除して、分けてみましょ

length q cmp lc;
print chr oct oct ord qw q do q; # 4
print chr oct oct ord q mkdir m; # 9
print chr oct oct ord qw q for q; # 6
print chr oct oct oct ord q eq le; # 1
print chr oct ord uc qw q bind q; # 6
print chr oct ord uc q each ne; # 5
print chr oct oct ord qw q dump q; # 4
print chr oct oct oct ord q eq ne; # 1
print chr oct oct oct ord q eq ne; # 1

こうしてみると、驚愕の事実が分かります。

「これ、、計算してない、、」


この時点ではどうして数字が表示されるのか、分かりませんが
素数戦争の答えである496165411をそのまま表示している事実が分かりました。
すげーマジカルな事やって計算してるのかなー って思ってたのに悔しい、、笑


ま、気を取り直して理解を進めます。
次に注目すべきはordという演算子と、その後ろの固まりです。

length q cmp lc;
print chr oct oct ord qw q do q; # 4
print chr oct oct ord q mkdir m; # 9
print chr oct oct ord qw q for q; # 6
print chr oct oct oct ord q eq le; # 1
print chr oct ord uc qw q bind q; # 6
print chr oct ord uc q each ne; # 5
print chr oct oct ord qw q dump q; # 4
print chr oct oct oct ord q eq ne; # 1
print chr oct oct oct ord q eq ne; # 1


その部分だけ実行してみましょう。

print ord qw q do q; # 100
print ord q mkdir m; # 107

これは下記のように評価されています。

print ord 'do';   #100
print ord 'kdir'; #107

これは私もよく分かっていないのですが、
qw q 文字列 q で '文字列' と評価されるようです。

または、
qwを書かない場合は q の次に来る文字から、またその文字までをシングルクォートで囲っていると評価するようです。
(perldocとかに書いてあるんでしょうか。探せなかったので誰かおしえてー(>_<) )

追記:
perldocに書いてありました。
「クォートとクォート風の演算子」のところですね。
Perlではqw{}の括弧は別に何でも代用が出来て、よく使うのだとqw//というのがありますね。
区切り文字が括弧ではない場合は前後で同じ文字があれば、それを区切り文字と評価するようです。
@tsucchiさんありがとうございます!


ordという演算子は引数の「一番先頭の文字」のASCIIコードを返します。

print ord 'A'; # 65
print ord 'Aiueo'; # 65 <-先頭のAしか評価されていない

なのでさっきのも

print ord 'd';    #100
print ord 'k';    #107

こうやってるのと一緒。
わざとPerl予約語のようにみせてただけなんですねー。


それらを踏まえると下記のようになります。

length q cmp lc;
print chr oct oct ord 'do'; # 4
print chr oct oct ord 'kdir'; # 9
print chr oct oct ord 'for'; # 6
print chr oct oct oct ord 'q l'; # 1
print chr oct ord uc 'bind'; # 6
print chr oct ord uc 'ach n'; # 5
print chr oct oct ord 'dump'; # 4
print chr oct oct oct ord 'q n'; # 1
print chr oct oct oct ord 'q n'; # 1

 

length q cmp lc;
print chr oct oct 100; # 4
print chr oct oct 107; # 9
print chr oct oct 102; # 6
print chr oct oct oct 113; # 1
print chr oct 66; # 6
print chr oct 65; # 5
print chr oct oct 100; # 4
print chr oct oct oct 81; # 1
print chr oct oct oct 81; # 1

oct演算子についてはこちらで詳しく説明されていました。
今回のケースでいえば引数を8進数とみなして、10進数への変換を行います。


chr演算子についてもこちら
ord演算子の反対ですね。ASCIIコードから文字に変換します。


oct演算子を2回、3回と使う事で、
目当ての数字にするためのASCIIコードにしているんですね。


最後に先頭にあった行について

length q cmp lc;

こちらは下記のように評価されます。

length 'mp l';

そして、lengthですから4という数値が返ります。
しかしprintしているわけではないので、そのまま何も表示されません。
dankogai.plはこの部分が無くても同じ結果になります。
きっと最初にprintが来てしまうと不思議さが半減してしまうからではないでしょうかw


今回ブログにまとめるにあたり、詳しく調べていたら
こういったコードがppencodeといわれる物だという事が分かりました。

とても参考になったページ
■TAKESAKOさんの ppencode が何で動くのか分かった。

2006年にTAKESAKOさんという方がYAPCで発表していたそうで、当時はだいぶ話題になっていたんですねー!


私はこのコードを会社の同僚と楽しみながら読む事が出来ましたし、
Perlへの理解が進んだように思います。
たくさんの人に感謝☆