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 ##)*""))
図にするとこんな感じ。
動かし方
github の README を参照してください。
スポイラー
普通の Quine は、こんな感じの構造です。
s = (自分自身のソースコード文字列を得る) puts s
一方、例えば何かを出力する Perl プログラム、を出力する Ruby プログラムはこんな感じです。
puts "print(\"...\");"
これと Quine を組み合わせれば、Ruby と Perl を行き来する multi-Quine のできあがり。
s = (自分自身のソースコード文字列を得る) puts "print(\"#{ s }\");"
theoretical にはこれを繰り替えしていっただけです。ただ、実際には以下のような practical な問題がありました。
- 文字列を適切にエスケープする必要がある
- Cobol や Fortran はプログラムのフォーマットに面倒な制限がある
- 左端に 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 の方が個人的に楽だったので。