Quine リレー

ref: https://github.com/mame/quine-relay/

以下は、自分自身を出力する REXX プログラムを出力する Python プログラムを出力する R プログラムを出力する (...略...) を出力する Scala プログラムを出力する Ruby プログラムです。合計 50 言語を使います。

eval$s=%q(eval(%w(B=92.chr;N=10.chr;n=0;e=->(s){s.gsub(/[#{B+B+N}"]/){B+(N==$&??n:$&)}};E=->(s){'("'+e[s]+'")'}
;d=->(s,t=?"){s.gsub(t){t+t}};D=->(s,t=?@){s.gsub(B){t}};Q=->(s,t=?$){s.gsub(t){B+$&}};puts(eval(%q("objectXQRX
extendsXApp{Hln#{E[%((displayX"#{e[%(HfX% sX"#{Q[  e["Trans  criptXshow:X'#{d[%(putsX[regsubX-allX{.}X"#{Q[e[%[
intXK(){sJXs=#{E[%(withXAda.Text _Io;p   roce     dure      XQRXisXbeginXAda.Text_Io.Put_Line("#{d[%(BEGINXH("#
{d[%(BEGIN{s=#{E[D[%(forXbXinX  Sys     t                            em.Text.ASCIIEncoding().GetBytes(#{Q[E["#i
nclude<stdio.h>`nintXK (){pu    t                                   s#{E["#include<iostream>`nintXK(){std::cout
<<#{E[%(usingXSystem;  cla                                                    ssXProgram{publicXstaticXvoidXMai
n(){Console.Write(#{    E                                                    [D[%((defnXf[lXr](if(>(countXr)45)
(lazy-seq(cons(str"                                                                 XXXX^""r"^"&")(fXl"")))(let
[c(firstXl)](ifXc(                                                                 f(nextXl)(if(=XcX^")(strXrXc
Xc)(strXrXc)))[(s                                                                       tr"XXXX^""r"^".")]))))(
doall(mapX#(Hln(                                                                       str"XXXXXXXX"%1))(lazy-c
at["IDENTIFICA                                    TIONXDIVI                                SION.""PROGRAM-ID.XQ
R.""PROCEDUR                              EXDIVISION."]#{%(s=#{E[%(                       packageXK;import("fmt
";"sJs");fu                           ncXK(){fmt.Print("H^x27"+sJs.Repla                   ce("#{e[D[e[%(import
XData.Cha                         r`nK=putStrLn$"procedureXK();write(^"DO,1                  <-#"++show(lengthX
s)++fXsX                        1X0;f(x:t)iXc=letXv=foldl(^aXx->a*2+(modXxX2))                0$takeX8$iterate(
flipXdi                       vX2)$Data.Char.ordXxXin(ifXmodXiX4<1then"PLEASE"el               se"")++"DO,1SUB#
"++sho                      wXi++"<-#"++show(mod(c-v)256)++"^^n"++fXt(i+1)v;f[]_X               _="PLEASEREADOU
T,1^^                      nPLEASEGIVEUP^");end";s=#{E[%(.classXpublicXQR`n.superXj              ava/lang/Objec
t`n.                     methodXpublicXstatic XK([Ljava/lang/SJ;)V`n.limitXstackX2`n             getstaticXjava
/lan                    g/System/outXLjava/i   o/PrintStream;`nldcX"#{e[%(classXQR{pub            licXstaticXvo
idX                    K(SJ[]v){SJXc[]=newX  S  J[8000],y="",z=y,s="#{z=t=(0..r=q=126).            map{|n|[n,[]
]};                   a=[];%(@s=internalXc  ons  tant[#{i=(s=%(PRINTX"#{Q["H#{E[%(all:`            n`t@HfX%sX"#
{e[       %(         .assemblyXt{}.method  Xstat  icXvoidXMain(){.entrypointXldstr"#{e["            varXu=requi
re(                 'util');u.H('#import  <stdio.  h>^n');u.H(#{E[D[%(intXK(){puts#{E["H_           sJ#{E["Hf#{
E[%(               say"#{e[                                     "programXQR(output);begin           Xwrite('#{[
*%(su        bXf  {$s="";for  each(sp  lit//,$_[0]){  $n=ord(  $_);$s.=substr(unpack("B8",          chr($n-($n<
58?46:       $n<91?53:59))),2  );}$s  =~s/.{7}/0$&/g;  HXpac  k("B".length($s),$s);}f"#{s=          %(<?phpXech
o"#{Q[e["   intXK(){write#{E["  qr:  -write('#{Q[e["H"  +E[  "cat"+E[%(eval$s=%q(#$s)).gsu          b(/.+/){"sa
yX`"#{d[$&]}`"" }]]],?']}'),nl,  h  alt."]};returnX0;}"  ]  ]}"?>);(s+N*(-s.size%6)).bytes          .map{|n|"%0
7b"%n}.join.sca n(/.{6}/).map{|n   |n=n.to_i(2);(n<12?n+   46:n<38?n+53:n+59).chr}*""}").s          can(%r(([X.
0-9A-Za-z]+)|(. ))).reverse.map  {  |a,b|(b)?"s//chrX#{  b  .ord}/e":"s//#{a}/"},"ev     a          l"]*"XxX"}'
);end."]}"`nend  `n)]}"]}"]};r  etu  rnX0;})]]}.replac  e(/  @/g,SJ.fromC               h          a  rCode(92)
))"]}"callXvoidX [mscorlib]Sy  stem.  Console::WriteL  ine(s  J)ret})]                  }          "   )]}",/[X
^`t;"(){}`[`]]/]  }`nBYE)).s  ize+1}X  xXi8]c"#{s.gs  ub(/[^`  n"]/){B+"%02`x          5          8     "%$&.or
d}}^00"declareXi3 2@puts(i8                                     *)defineXi32           @          K      (){sta
rt:%0=callXi32@pu  ts(i8*XgetelementptrX  inbound  s([#{i}XxXi8]*@s,i32X0,i3 2X0))ret X          i       32X0})
.bytes{|n|r,z=z[n]  ||(a<<r;q<5624&&z[n]=  [q+=1  ,[]];t[n])};a<<r;t=[*43..123]-[64, *          9         2..96
];a.map{|n|t[n/75].  chr+t[n%75].chr}*""}"  ;in  tXi,n,q=0,t;for(n=0;++n<126;)c[n]=""          +          (char
)n;for(i=0;++i<s.len  gth();){t=s.charAt(i)  ;  q=q*75+t-t/64-t/92*5-43;if(i%2>0){y=          q           <n?c[
q]:y;c[n++]=z+=y.char   At(0);System.out.H(z   =c[q]);q=0;}}}})]}"`ninvokevirtualX           j             ava/
io/PrintStream/Hln(Lja   va/lang/SJ;)V`nretur n`n.endXmethod)+N]})]]]}^x27^n","@"           ,              "^^"
,-1))})]};u="XXXXXXXX";    g=(l)->l.replace(/[^^"]/g,(x)->"^^"+x)`nf=(l)->conso           le.log(          "(wr
ite-lineX^""+g(l)+"^")")`    ne=(l)->f(".^^^""+u+g(l)+"^"Xcr")`nd=(l)->e("WRI           TE(*,*)'"          +u+l
+"'");d`nd("programXQR")("      HX^"(&");i=0`nd("&A,&")whileXi++<s.length`            nd("&A)^",&");i=0`n  d("&
char("+s.charCodeAt(i++)+"),       &")whileXi<s.length`nd("&^"^"")("end             XprogramXQR");e("STOP"  );e
("END");f("bye")).gsub(/.+/){%(        (cons"DISPLAY"(f"#{e[$&]}"""               )))}}["STOPXRUN."])))),?  ~]]
}.Replace("~","^^"));}})]};}"]};r             eturnX0;}"]]}                    ):HXjoin(['+'forXiXinXrange(0,b)
],"")+".>"),?!]]};gsub(/!/,"^^",s);HX                                       s})]}")END)]}");endXQR;)]};intXi,j;
H("moduleXQR;initialXbeginX");for(i=1;i<=                              s.length;i++){H("$write(^"XXX");for(j=6;
j>=0;j--)H((s[i-1]>>j)%2>0?"^^t":"X");H("^^n^^t^                ^nXX^`");");}H("$display(^"^^n^^n^");endXendmod
ule");returnX0;}].reverse],/[`[`]$]/]}"X^x60.&];putsX"k"),?']}';cr"]]}")]}"))]}}").gsub(/[HJK^`X]/){[:print,0,:
tring,:main,B*2,0,B,32.chr][$&.ord%9]})) ##  Quine Relay -- Copyriht (c) 2013 Yusuke Endoh, @hirekoke  ##)*""))

図にするとこんな感じ。

uroboros

動かし方

github の README を参照してください。

スポイラー

普通の Quine は、こんな感じの構造です。

s = (自分自身のソースコード文字列を得る)
puts s

一方、例えば何かを出力する Perl プログラム、を出力する Ruby プログラムはこんな感じです。

puts "print(\"...\");"

これと Quine を組み合わせれば、RubyPerl を行き来する multi-Quine のできあがり。

s = (自分自身のソースコード文字列を得る)
puts "print(\"#{ s }\");"

theoretical にはこれを繰り替えしていっただけです。ただ、実際には以下のような practical な問題がありました。

  • 文字列を適切にエスケープする必要がある
    • 古い言語は C 言語風のエスケープシーケンスをサポートしてないことが多い
    • 言語によっては interpolate をエスケープする必要もある
  • CobolFortran はプログラムのフォーマットに面倒な制限がある
    • 左端に 7 つほどスペースが必要とか、一行は最大 80 文字未満とか
  • ナイーブにエスケープすると途中のファイルがでかくなりすぎる
    • JVM の String サイズの 64KB 制限にひっかかる
    • Intercal や Whitespace など一部の言語で耐えがたいくらい遅くなる

これらを回避するため、Perl で ppencode みたいなことしてたり、Java では LZW 圧縮したりして、使用文字制限+圧縮してます。Cobol の横幅制限は Clojure で、Fortran の横幅制限は、Coffeescript でプログラム生成して回避してます*1。詳細に興味がある人は生成スクリプトを見てください。

裏話

以前の Quine リレーが古くなって動かなくなったらしいので、作り直したものです。ただ作り直すだけでは面白くないので、手当たり次第に言語を増やしました。
生成スクリプトも公開したので、次に動かなくなった時は誰かがメンテナンスしてくれると期待します。
原則として Ubuntu で簡単にインストールできる処理系のみを対象にしましたが、以前の版で使っていた Unlambda と Whitespace が残念ながら apt でインストールできなくなってしまったようなので、簡単なインタプリタを付けました。一応本家の実装でも動作確認しています。
結構コードサイズがでかいです。ちゃんと圧縮すれば小さくできますが、各言語のコード断片が透けて見える方がおしゃれだと思ったので、あえてこのままで。

メモ

よく知らない言語処理系をあれこれ apt-get install したくない、という場合は debootstrap + chroot で試すとよいです。

# debootstrap raring sandbox
# chroot sandbox
# mount -t proc proc /mnt/debinst/proc # これをしないと apt-get に失敗する
# vi /etc/apt/sources.list # restricted universe multiverse を足す
# apt-get update
# apt-get install ...

*1:Fortran は直前の Forth で回避する方がいいのですが、複雑なプログラムを書くのは Forth より Coffeescript の方が個人的に楽だったので。