Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Quine・難解プログラミングについて

 Quine・難解プログラミングについて

第 379 回 PTT の発表で使った資料です。
http://www.ci.i.u-tokyo.ac.jp/~sasada/ptt/arc/379/

Yusuke Endoh

November 23, 2011
Tweet

More Decks by Yusuke Endoh

Other Decks in Programming

Transcript

  1. 自己紹介: 遠藤侑介 • 会社員 – 本発表は完全に趣味の内容です • Ruby コミッタ –

    Ruby 2.0 リリースマネージャ (2013年リリース予定) – 本発表はこの内容でもありません • esoteric programmer ← 今日のテーマ – Twitter: @mametter – Blog(日本語): http://d.hatena.ne.jp/ku-ma-me/ – Blog(英語): http://mamememo.blogspot.com/ 2
  2. Esoteric Programming • 普通の人 • Esoteric Programmer • 参考: code

    golfer (14B) alias|send¥ ;$stdin=GC | "%p?"%def# FALSE.gets();(8 | 64).chr+232424. to_s(25)+", "+%w|w ! | *"orlc". next<<012|| (c).Yusuke end;"oh, 2009" | "stegano-X."[0,4].reverse d,be="whydoes","crypto";:make. | %.mains..tr'eams',be.delete(d) puts “Hello, world!” #!../s/grb -eh http://d.hatena.ne.jp/kurimura/20090929/1254257912 3
  3. Esoteric Programming Language (esolang) • 読み書きしにくいように作られた言語 – esoteric: 難解な、深遠な、秘伝的な、奥義に達した –

    ex) brainf**k, whitespace • 共通の特徴:奇妙な制約 – 使える文字・レイアウト – 使える命令 – 変な意味論 • Ruby も制限をすれば esolang になる – 使用文字の制限とか +++++++++[>++++++++>++++++++++ +>+++++<<<]>.>++.+++++++..+++. >-.------------.<++++++++.---- ----.+++.------.--------.>+. Hello world in brainf**k 4 Hello world in whitespace
  4. Gödel 数によるコードの数値化 • 任意の文字を自然数にエンコードする – in: 𝑥1 , 𝑥2 ,

    𝑥3 , 𝑥4 , 𝑥5 , 𝑥6 , 𝑥7 , … – out: 2𝑥1 × 3𝑥2 × 5𝑥3 × 7𝑥4 × 11𝑥5 × 13𝑥6 × 17𝑥7 × ⋯ – デコードは素因数分解 • 実際には以下のようにしている – out: 2560𝑥1 + 2561𝑥2 + 2562𝑥3 + 2563𝑥4 + ⋯ – 0 ≤ 𝑥𝑖 < 256 とわかっているのでこれで十分 9 数字だけで Ruby
  5. コードの数値化の実装 • エンコード – num = str.unpack(“H*”)[0].hex • “abc”.unpack(“H*”) #=>

    [“616263”] • “616263”.hex #=> 0x616263 = 6382179 • デコード – [num.to_s(16)].pack(“H*”) • 6382179.to_s(16) #=> “616263” • [“616263”].pack(“H*”) #=> “abc” • エンコード時にシグネチャをつけておく – (sig + str).unpack… – 理由は次ページ 10 数字だけで Ruby
  6. Ruby の黒魔術 • コードに書かれただけの数字を引っぱり出す – GC.disable – at_exit – ObjectSpace.each_object(Bignum)

    – eval • 無関係なBignumも拾えてしまう – シグネチャで判断する – チェックサムもつけとけばよかった  Ruby 1.8 と 1.9 両方で動く  JRuby でも動く(が –X+O オプションが必要) 数字だけで Ruby 11
  7. アンダースコアだけで Ruby require "_" ____ _ _____ ____ __ ____

    ____ __ ___ ____ __ __ _ ______ _____ ___ _ _ ___ _____ ______ ____ _ _ ____ _ _ ____ _ ____ __ __ ___ _ ______ ___ ____ __ ______ ____ _ ____ ____ __ _ ____ _ _ ___ _____ _____ _ ______ ____ _ ______ _____ 12
  8. 実装 • プログラムの数値化(「数値だけ」と同じ) • ゲーデル数を 6 進表記し、各桁をアンダースコアの 数で表す – 

    • _ や __ はメソッド呼び出しなので普通に拾える – ただし、 は なので、評価順に注意 • Ruby は Call-by-value なので 無引数 、引数あり 、無引数 の順に呼ばれる • 引数の有無を見て の順に直す アンダースコアだけで Ruby 13 _ __ ___ _(__) ___ __ _ ___ _ __ ___ 123 _ __ ___
  9. なぜ6進数か? • 生成されるコード長を短くするため – コードを数値化した値: M – n進数で表現した時の桁数: log𝑛 𝑀

    – 各桁は 1~n 文字のアンダースコア+空白: 平均 𝑛+3 2 – 生成される平均コード長: 𝑛+3 2 log𝑛 𝑀 – 𝑛 = 5 で最小 • ただし計算間違いして 𝑛 = 6 で公開してしまった – gem install _ で公開中 14 Hello world with underscore only
  10. 純粋にアルファベットだけで Ruby class String def inspect concat begin dup ensure

    replace String nil concat concat concat concat size concat concat size concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat concat concat concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat concat concat size concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat concat size concat concat size concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat size concat concat concat concat size concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat concat size concat concat size concat concat size concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat size concat concat concat size concat concat size concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat concat concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat concat size concat concat size concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat concat size concat concat size concat concat size concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat concat size concat concat size concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat concat size concat concat size concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat concat concat size concat concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat concat concat size concat concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat concat concat size concat concat size concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat concat concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat size concat concat concat concat size concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat size concat concat concat size concat concat concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat size concat concat concat size concat concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat size concat concat size concat concat concat concat concat size concat begin size ensure replace String nil end end eval self exit end end copyright MMX Yusuke Endoh p String nil • require がない class String def inspect concat begin dup ensure replace String nil concat concat concat concat size concat concat size concat concat size concat size concat begin size ensure replace String nil end end concat begin dup ensure replace String nil concat concat concat concat concat concat size concat size concat begin size ensure replace String nil end end 15
  11. 第一段階: アルファベットと数字だけで Ruby • 浜地慎一郎さんのアイデア http://d.hatena.ne.jp/shinichiro_h/20081109 • p String nil

     p(String(nil))  p(“”)  puts(“”.inspect) • “”.concat 122  “p” • “p”.concat 32  “p “ • “p “.concat 49 “p 1” • eval self  eval “p 1” 純粋にアルファベットだけで Ruby 16 # p 1 に相当するコード class String def inspect concat 122 # ‘p’ concat 32 # ‘ ‘ concat 49 # ‘1’ eval self exit end End P String nil
  12. 第二段階: アルファベットだけで数字を作る(1) • caller – スタックトレースを Array として返すメソッド • [“t.rb:2:in

    `f2'", • "t.rb:3:in `f3'", • "t.rb:5:in `<main>‘”] – メソッド大量でかっこ悪い • for 文 – ary.each {|i|……} • の構文糖 • Rubyでは使われない 構文として有名 – eachをsizeのエイリアス にすると、for文でsize が呼べる 17 # 3 を作るコード def f1; caller; end def f2; f1; end def f3; f2; end f3.size 純粋にアルファベットだけで Ruby # . なしでメソッドを呼ぶ class Array alias each size end p for i in f3 do end
  13. 第二段階: アルファベットだけで数字を作る(2) • 長さ n の文字列を作る – concat size:長さを+1 –

    concat self:長さを×2 – O(log(n)) で表現できる • コード文字列を生成する部分と 衝突する – begin A ensure B end で 回避 • A を評価して B を評価して A の評価結果を返す 18 # 4 を作るコード class String def inspect concat size concat self concat self size end end 純粋にアルファベットだけで Ruby # “¥4”を作るコード concat begin dup ensure replace "" concat size concat self concat self concat begin size ensure replace "" end end
  14. 「1.使用文字を制限して Ruby を書く」のまとめ • Ruby は使用文字に結構強い • 未解決問題 – 小文字アルファベットだけ

    – 大文字だけ(不可能?) – 「記号だけ」は解決済み http://www.kurimura.com/rsencode/ 19
  15. Quine とは • 自分自身を出力するプログラム • 実装の基本 – 手順 1. 自分自身を文字列として再構成する

    – 手順 2. その文字列を出力する • 手順 2 で整形をすると美しい Quine が書ける eval s="puts'eval s='+s.inspect" 21
  16. アスキーアートでプログラムを書く 1. 空白とバックスラッシュなしでプログラムを書く 2. と で囲む 3. 自由に整形できる • 注:

    %w() は文字列配列のリテラル • = • 元の形を保持したい場合には、二重に eval する 山手Quine eval %w( pu ts “H i” ).join## eval %w( ( ).join %w(foo bar) [“foo”, “bar”] 24 eval s=%q(eval %w( ... ).join)
  17. 圧縮 • データサイズとデコーダサイズのトレードオフ • 最適な圧縮方法はデータの大きさや使用文字によっ て変わる • 経験的な方法 山手Quine データサイズ

    方法 ~ 10 バイト 無圧縮で文字列リテラル ~ 100 バイト String#to_i(36) ~ 500 バイト Base64 もっと Zlib + pack デコーダ小 データ大 データ小 デコーダ大 25
  18. quineclock eval s=%w(0;y=""< <32;z="eval((;s=%w(#{s} )*''));; %" <<43<<s; d="";" 0v vvnvn2

    mj4kil 0v lvvul2 6l94ol 0v lv7vv" .scan (/ .{7}/ ){|n| 2. times {d.< <T ime. now. to_s.unpack(" C*") [11, 8].map{|i|("% 03b" .%7&n .to_i (32)> >3*i- =48).g sub(/. /){s>$ &?y*3: z.slic e!(0,3 )}}*y<<1 0}};puts (d.strip.chop<<43))*''# #(c) Y.Endoh 2009 26
  19. 実装 eval s=%w(0;y=""< <32;z="eval((;s=%w(#{s} )*''));; %" <<43<<s; d="";" 0v vvnvn2

    mj4kil 0v lvvul2 6l94ol 0v lv7vv" .scan (/ .{7}/ ){|n| 2. times {d.< <T ime. now. to_s.unpack(" C*") [11, 8].map{|i|("% 03b" .%7&n .to_i (32)> >3*i- =48).g sub(/. /){s>$ &?y*3: z.slic e!(0,3 )}}*y<<1 0}};puts (d.strip.chop<<43))*''# #(c) Y.Endoh 2009 27 Quine 部分 フォントデータ 現在時刻取得 デジタル時計の形に整える 出力 コピーライト quineclock
  20. 基数変換によるフォント圧縮 • 元データ (横 3 × 縦 5 × 文字数

    11 = 165 バイト) • 空白を 0 、# を 1 にして二進数に (165 バイト) • 10 進表示に (50 バイト) • to_s(36) で36進数に (35 バイト) • 復元には “%b” % n や Integer#[] が便利 28 quineclock ### ## ### ### # # ### ### ### ### ### # # # # # # # # # # # # # # # # # # ### ### ### ### ### # ### ### # # # # # # # # # # # # # # ### ### ### ### # ### ### # ### ### 111 110 111 111 101 111 111 111 111 111 000 101 010 001 001 101 100 100 001 101 101 010 101 010 111 111 111 111 111 010 111 111 000 101 010 100 001 001 001 101 010 101 001 010 111 111 111 111 001 111 111 010 111 111 000 1073733623, 2905756245, 1063256021, 2371130133, 1063231487 0hr9u5z, 1c20fv9, 0hl19lh, 137pjpx, 0hl0qnz
  21. Qlobe: 回る地球の Quine v=0000;eval$s=%q~d=%!^Lcf<LK8, _@7gj*LJ=c5nM)Tp1g0%Xv.,S[<>YoP 4ZojjV)O>qIH1/n[|2yE[>:ieC "%.#% :::##" 97N-A&Kj_K_><wS5rtWk@*a+Y5 yH?b[F^e7C/56j|pmRe+:)B

    "##% ::##########" O98(Zh)'Iof*nm.,$C5Nyt= PPu01Avw^<IiQ=5$'D-y? "##: ###############" g6`YT+qLw9k^ch|K'),tc 6ygIL8xI#LNz3v}T=4W "# #. .####:#######" lL27FZ0ij)7TQCI)P7u }RT5-iJbbG5P-DHB<. " ##### # :############" R,YvZ_rnv6ky-G+4U' $*are@b4U351Q-ug5 " #######################" 00x8RR%`Om7VDp4M5 PFixrPvl&<p[]1IJ " ############:#### %#####" EGgDt8Lm#;bc4zS^ y]0`_PstfUxOC(q " .#############:##% .## ." /,}.YOIFj(k&q_V zcaAi?]^lCVYp!; " %% .################. #. " ;s="v=%04o;ev"% (;v=(v-($*+[45, ":####: :##############% : " ])[n=0].to_i;)% 360)+"al$s=%q#{ "%######. ######### " ;;"%c"%126+$s<< 126}";d.gsub!(/ "##########. #######% " |¥s|".*"/,"");; require"zlib"|| "########### :######. " ;d=d.unpack"C*" d.map{|c|n=(n|| ":#########: .######: . " )*90+(c-2)%91}; e=["%x"%n].pack " :#######% :###### #: " &&"H*";e=Zlib:: Inflate.inflate( " ######% .####% :: " &&e).unpack("b*" )[0];22.times{|y| " ####% %### " ;w=(Math.sqrt(1-( (y*2.0-21)/22)**(; " .###: .#% " ;2))*23).floor;(w* 2-1).times{|x|u=(e+ " %## " )[y*z=360,z]*2;u=u[ 90*x/w+v+90,90/w];s[( " #. " ;y*80)+120-w+x]=(""<< 32<<".:%#")[4*u.count(( " . " ;"0"))/u.size]}};;puts¥ s+";_ The Qlobe#{" "*18+ ( "# :#######" ;"Copyright(C).Yusuke End¥ oh, 2010")}";exit~;_ The Qlobe Copyright(C).Yusuke Endoh, 2010 29
  22. 実装 • 世界地図のデータを持っている • データ  zlib 圧縮  巨大な整数化

     コード文字列化 • 後述 • 出力前に地図データをデコード・レンダリングする • 地球の絵がコードに割り込んで来るのでかわす Qlobe 30 require“zlib”|| “地球部分" ;d=d.unpack"C*" Inflate.inflate( “地球部分" &&e).unpack("b*"
  23. Bignum のコード文字列化 • 表示可能な文字だけでエンコードする • ただし、出現位置によって使えない文字がある – 文字列リテラルのデリミタとして使っている文字 • 普通の言語では

    “ (34) • 今回は ! と “ と ~ を使っている(33, 34, 126) – エスケープ文字 ¥ (92) – Ruby の場合、# (35) にも注意が必要 • “user id: #{ id }” と書ける • 今回の地図データではたまたま #{ の部分列が出現しなかった • 今回は 35 ~ 91、93 ~ 125 の 90 種類の文字を使う – 90 進表記にして、それぞれの桁を各文字に割り当てる • n ∈ {0 .. 89} に対して、 m = ((n+58)%91)+35 の文字を使う • デコード: (m - 2) % 91  n 31 Qlobe
  24. 15quzzle Quine でパネル移動後の盤面が出てくる eval$s=%w[b=0 x40e1359a76cb d8f2;i=(m=0.. 15).find{|i|1 >b&m=15<<4*i} ;t=m|n=m<<4*o =("AdABrBlBAu

    A"=~/(.)#{ARG V*''}¥1/||04| |0)-4;(n<1||n >1<<64||[255< <12]&[t>>040| |___________2 |__________15 |___________8 |__________13 |0,t>>16,t]!= [])?t=0:i+=o; ;s="eval$s=%% w[b=0x%016x"% (b^=t.&b|m&b> >o*4)+$s.gsub (/(¥|_+¥d+)+/ ,'')[/;.*/]+" ]*''||0"<<92| |1;z=s=s.scan (/.{13}/);3.t imes{|j|s[(i| |__________11 |__________12 |___________6 |___________7 |0)/4*8+i+j*4 ,0]=m=(z=32.c hr)*13};c=b;4 .times{puts(( 0..3.times{pu ts((s.slice!( 0,4)*z).rstri p)}).map{j=c% 16;c/=16;;(0| |0)<(j)?"|"+j .to_s.rjust(1 2,"_"):m}*(z| |__________10 |___________9 |___________5 |___________3 |0),z)};b==0x fedcba9876543 21&&("%b"%"1t v7c1th0wylel7 3ba35knw3t".t o_i(36)).tr(" 01",".#").sca n(/.{25}/){pu ts$&}]*''||0¥ |___________1 |__________14 |___________4 32
  25. QR code: Q(uine) R(uby) code(解説) #!ruby # Q(uine)R(uby)code (C) Y.Endoh

    2009 eval s=%q(X=(0..7).map{|i|1<<i};W=116;m=(1..w=117).map{[]};B=999.times{|i|X<<( X[-4]^X[-5]^X[-6]^X[-8]);n=0x7f415d5d5d417f;i<64&&m[j=i/8][k=i%8]=m[W-j][k]=m[ j][W-k]=n[i]};P=6.step 110,26;P.map{|j|P.map{|k|m[j][k]||25.times{|i|m[j-2+i/5 ][k-2+i%5]=-469441[i]}}};g=[1];m[109.times{|i|m[i][6]=m[6][i]||=1-i;i<15&&m[i. +i<6?0:i<8?1:102][8]=m[8][(i>8?14:i>7?15:W)-i]=26998[i];i<18&&m[i/3][x=i%3+106 ]=m[x][i/3]=102881[i];i<26&&g=(g+[0]).zip([0]+g.map{|x|X[i+X.index(x)]}).map{| x,y|x^y}}][8]=1;_,*G=g;n=c=12704;b=("404e4#{"%02x"*1252}0#{"ec11"*B}"%%(#!ruby # Q(uine)R(uby)code (C) Y.Endoh 2009¥neval s=%q(#{s})).unpack("C*")).scan /../ D=[];E=([106]*8+[107]*4).map{|d|D<<y=b.shift(d).map(&:hex);z=y+G.map{0};y.map{ r=X.index z.shift;r&&z=z.zip(G).map{|y,x|x ?y^X[r+X.index(x)]:y}};z};require"¥ zlib";(D*B+E*B).map{|l|x=l.shift;x&&n=x+n*256};x=13688;d=[1,W];x.times{m[i=x/w ][j=x%w]||=n[c-=1]^i+j+i*j%3+1;x-=y=d.shift;d<<y;0<=x&&x<w*w||x-=3+d[1]=-2-y ; x==6&&x=5};z,e="¥0","¥xff";t=(z+e*125)*16;s="IDAT"+Zlib::Deflate.deflate(t+m . map{|l|[z,e*4,l.map{|x|(e+z)[x&1,1]},e*4]*4}*""+t);print ["¥x89PNG¥r¥n¥x1a¥n", 13,"IHDR",500,500,2,0,2752354551,s.size-4,s,Zlib.crc32(s),0,"IEND¥xAEB`¥x82"]. pack"A*NA*NNCN3A*NNA*") 36 shebangとコピーライト 原始多項式生成 QRコードの大きなマークの配置 QRエンコード Quine部分 QRコードのデータ部分の配置 png生成
  26. tkquine eval$s=%q(require"tk" r=Tk.root;r.withdraw x=y=22;s="eval$s=%q(#$s)" f=proc{c=s.slice! 0,1 (f[y+=x=22];next)if"¥n"==c w=TkToplevel.new(r){ withdraw geometry"18x18+#{x}+#{y}"

    overrideredirect true bind"Button",proc{exit} deiconify } l=TkLabel.new(w,:text=>c) l.pack;x+=22 Tk.after(10,f)if s!="" };f[];Tk.mainloop #(c)YusukeEndoh2010) 37
  27. quine-web-server 40 eval$e=%q(puts'open http://localhost:18463';require'socket' s=TCPServer.new 18463;c=(/GET +¥/(¥d*)/i=~c.gets;c<<%(HTTP¥ /1.0 200 OK¥nContent-Type:text/html¥n¥n)+(eval(%(#$1%

    60>0? %(leval$e=%q(¥#$e)#)[#$1,1].sub('&','&amp;').sub('>','&gt;' ).sub('<','&lt;'):'<br>'# ;nil))||%(<title>quine web server </title>The source code of this web server is:<pre id='p'>¥ </pre><script type='text/javascript'>a=i=0;f=function(){a!= 0&&a.readyState<4||(a!=0&&(document.getElementById('p').in¥ nerHTML+=a.responseText),i++<719&&(a=new XMLHttpRequest() , a.open('GET','/'+i,true),a.onreadystatechange=f,a.send(null )))};f()</script>));puts$_;c.shutdown;c.read;c.close)while¥ c=s.accept ## quine-web-server.rb (c) Yusuke Endoh 2009 #)#
  28. QB: Q(uine ru)B(y) eval s||= %q(1;t= %w(s.gs ub!(/¥e.* ?m/,"");[ [

    0x26e885c a844a879e27 a,519,48, 5 , 9],[20182686,600,48,5,1],[4366,611,48 , 5 ,15],[432803624050570,533,0]].map { | r,m,*d|s[m+=r%74,0],r="¥e[#{d*";"}m", r /74while(r>0)};puts((z=32.chr)*5+"eval#{z *39}s||=¥n#{z*5}%q(#{(1+n=s.to_i)%3}#{s[1 ..- 1]})¥n¥n"+z*9*n+"5YOV44GvUnVieWlzdO OBq +OB quOBo+OBpuOBj+OCjOOBo+OBpuOAgeOBjeOBo eOC k+OB qOOBi umhmOOBhOOBl+OBn+ OBr+O BmuO BoOO CiO+8 nw ogIOWun+mam+OBr ui ogOiq nuS7 leanm OOBjO OBqeOBhuOBhOOBh uOCgu OBruO Bi+OA geiqrO aYjuOCkuecgeeVpeO Bl+OBn +OBke OCjOOB qeOAgi/lg5XjgajlpZHntITjgZfjgabjgIFSdWJ 5aXN04 4Gr44G q44Gj44Gm5qyy44GX44GE44KT44GgL+WQm+OBn+ OBoeOB r+OBhOOB pOOCguOBneOBhu O B oOOBreOAggrkup Lmj5vmgK fjgafjg 4/jg57jgovjgaj m sbrjgb7jgaPjga blkIzjg Zjlj43lv 5zjgpLjgZnjgovjgILoqLPjgYzliIbjgY vjgonjga rjgYTjgo g=".gsub(/¥e.*?m/,"").unpac k("m")[0 ].split( "/")[n]);exit)*"";; eval(t)) 42
  29. Quine リレー このプログラムは、このプログラム自身を出力する Unlambda プログラム、を出力する Whitespace プログ ラム、を出力する brainfuck プログラム、を出力する

    Java プログラム、を出力する C プログラム、を出力す る Haskell プログラム、を出力する OCaml プログラム、 を出力する Lua プログラム、を出力する Perl プログラ ム、を出力する Python プログラム、を出力する Ruby プログラム、です 45
  30. Quine リレー $ ruby QuineRelay.rb > QuineRelay.py $ python QuineRelay.py

    > QuineRelay.pl $ perl QuineRelay.pl > QuineRelay.lua $ lua QuineRelay.lua > QuineRelay.ml $ ocaml QuineRelay.ml > QuineRelay.hs $ runghc QuineRelay.hs > QuineRelay.c $ gcc -Wall -o QuineRelay QuineRelay.c $ ./QuineRelay > QuineRelay.java $ javac QuineRelay.java $ java QuineRelay > QuineRelay.bf $ beef QuineRelay.bf > QuineRelay.ws $ wspace QuineRelay.ws > QuineRelay.unl $ unlambda QuineRelay.unl > QuineRelay2.rb $ diff QuineRelay.rb QuineRelay2.rb 46
  31. 「2. Ruby で美しい Quine を書く」のまとめ • Ruby は文字配置制約+Quine に滅法強い •

    あやしい言語機能に感謝 • %w, %q • BEGIN • eval – 文字列整形も強い • String#rjust, split, gsub!, slice!, rstrip • to_i / to_s / Integer#[] • unpack / pack / zlib 48
  32. esolang と Quine • esolang: わざと難解に作られたプログラミング言語 • 一番有名な esolang: Brainf*ck

    – 現在最短 Quine (404B) • http://d.hatena.ne.jp/KeisukeNakano/20070626/1182879045 – Brainf*ck 派生言語汎用 Quine 生成器 • http://d.hatena.ne.jp/ku-ma-me/20100322/p1 • esolang での Quine の書き方 – Ruby の eval のような便利なものはない場合が多い – よくあるアプローチ: A+B+C • コード A: 「コード B + Cを表現するデータ」をメモリに載せる • コード B: メモリデータをデコードし、再エンコードして出力する • コード C: メモリデータをデコードして出力する 50
  33. Piet • 画像でプログラミングする esolang – ポインタが 2 次元に駆け回る – 色が異なる領域への移動が命令になっている

    • push は、移動前領域の大きさの数値を push する – 命令自体は普通のスタックマシン 51 色相\明度 変化なし 1段階変化 2段階変化 変化なし push pop 1段階変化 add subtract multiply 2段階変化 divide mod not 3段階変化 greater pointer switch 4段階変化 duplicate roll in (number) 5段階変化 in (char) out (number) cut (char) Hello world in Piet
  34. コード B + C Piet での Quine (上部拡大) 53 GIF

    ヘッダを 出力する コードA: 上の部分の ビットマップを スタックに載せる コードC: ビットマップを 無圧縮GIFで出力する コードB: ビットマップを 再エンコードして 出力する もう一回コード A を 実行する コード A 出力
  35. Grass • Wとwとvだけで関数適用を表現する関数型esolang • Grass の Quine 短縮の歴史 – 2008/09/10:

    164MB (shinh 、たぶん世界初) – 2008/09/11: 9787B (mame) – 2008/09/14: 1525B (kikx) • http://d.hatena.ne.jp/kikx/20080914 • W を WWw 、w を Ww にエンコードして表現する 54 Applications : WWWWWwwwwww WWWWWWwwwwwww ... WWWWwwww WW ... |<-5-><-6-->|<-6--><--7-->| | 4 4 | | | | | | | (5, 6) | (6, 7) | | (4, 4) | | apply | apply | | apply |
  36. Grass で Quine (kikx さんによる) 55 wwWWwWWWwWWWWWWwvwwWWwWWWwvwWWwwwWwwwwwwWWWwWWWWWwwwwWwwWWwWwwwwwww wwwwwvWwwwWWwwwvwwWWWWWWWWwwvWwwwWWwwwwwwwwwwWWWwwwwvwWwwwwWwwwwWww wwvwWWWwWWWWWwvwWWWWwWWWwvwWWWWWWWwWWWwvwWWWWWWwWWWWwvwWwwWwwwwwwWw wwwwwvwWwwwwwwwWWwwwWWWWWWwvwwwwWWWWwwWwwWwwwwwvwwvwwwwwWWWWwwwWwwW

    wwwWwwwwwwwwwWWWWWWWWWWWwwwwwwwWWWWWWWWWWwwwwwwwwwwWwwWwwwwwwwwwwwv wwwwWWWWWwwwwWwwwWWWwwwwwwwwWwwWwwwwwwwvwwwwWwwwWwwwWwwwwwwvwwWWWWw wwwWwwwwWwwwWWWWWwvwWWwWWWWWWWWWwvwWwWwWWwWWwWwWWwWWwWWwWwWWwWWwWWw WWwWWwWWwWwWWWwWwWwWWwWWwWwWWwWWwWWwWwWWWwWwWWwWWwWwWwWwWWwWwWwWwWw WwWwWWwWWwWWwWwWWwWWwWWwWWwWWwWwWwWwWwWWwWwWwWWwWWwWwWWwWwWwWwWwWwW wWwWwWwWwWwWwWWWwWWwWwWwWwWWwWWwWwWwWwWWWwWwWwWWwWWwWWwWWwWWwWWwWWw WWwWwWwWWWwWWwWwWwWwWWwWWwWwWwWwWwWwWwWwWwWwWwWWwWWwWWwWwWwWwWwWWWw WwWWwWwWwWwWwWWwWwWwWwWwWWwWwWwWwWwWWWwWwWWwWWwWWwWwWWwWWwWWwWWwWWw WwWWWwWwWWwWWwWWwWWwWwWWwWWwWWwWwWWWwWwWWwWWwWWwWWwWWwWWwWWwWwWWwWW wWWwWwWWWwWwWWwWWwWWwWWwWWwWWwWwWWwWWwWWwWWwWwWWWwWwWWwWwWwWWwWwWwW wWwWwWwWWwWwWwWwWwWwWwWWWwWwWWwWwWwWwWwWwWwWwWWwWWwWwWwWwWWwWWwWWwW WwWWwWWwWwWWWwWwWwWwWwWWwWWwWWwWWwWwWwWWwWwWwWWwWwWwWwWwWwWWWwWwWwW WWwWwWwWwWwWwWWwWWwWWwWWwWwWwWwWWwWwWwWWwWwWwWwWWwWwWwWwWwWwWwWwWwW wWWwWWwWWwWWwWWwWWwWWwWWwWWwWWwWWwWwWwWwWwWwWwWwWWwWWwWWwWWwWWwWWwW WwWWwWWwWWwWwWwWwWwWwWwWwWwWwWwWWwWwWwWWwWwWwWwWwWwWwWwWwWwWwWwWWWw WwWwWwWwWWwWWwWWwWWwWWwWwWwWwWwWWwWwWwWwWWwWWwWWwWwWwWwWwWwWwWwWwWW wWwWwWWwWwWwWwWwWwWwWwWWWwWwWwWwWwWWwWwWwWwWWwWwWwWwWWwWwWwWwWwWwWw WWWwWwWwWWwWWwWWwWWwWwWwWwWwWWwWwWwWwWwWWwWwWwWwWWwWWwWWwWWwWWwWwWW WwWwWWwWWwWwWWwWWwWWwWWwWWwWWwWWwWWwWWwWwWWWwWwvWWw
  37. ModanShogi での Quine • 将棋の棋譜風の Quine 56 *6 ▲1一龍 △2一龍

    ▲1一龍 △2一龍 ▲1一龍 △2一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一 龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1二飛 *7 △2一龍 ▲1一龍 △1一龍 ▲1一龍 △ 2一龍 ▲2一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲ 1一龍 △1二飛 *8 ▲2一龍 △1一龍 ▲2一龍 △1一龍 ▲2一龍 △2一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1二飛 *9 △2一龍 ▲1一龍 △2一 龍 ▲1一龍 △2一龍 ▲2一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一 龍 △1一龍 ▲1一龍 △1二飛 *10 ▲2一龍 △1一龍 ▲1一龍 △1一龍 ▲2一龍 △2一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1二飛 *11 △1 一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲2一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1 一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1二飛 *12 ▲2一龍 △2一龍 ▲1一龍 △1一龍 ▲2一龍 △2一龍 ▲1一龍 △2一龍 ▲2一龍 △1一龍 ▲2一龍 △1一龍 ▲1一龍 △2一龍 ▲1一龍 △1一龍 ▲1二飛 *13 △2一龍 ▲2一龍 △1一龍 ▲1一龍 △2一龍 ▲1一龍 △1一龍 ▲1一龍 △2一龍 ▲2 一龍 △2一龍 ▲2一龍 △2一龍 ▲2一龍 △2一龍 ▲2一龍 △1二飛 *14 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △2一龍 ▲2一龍 △2一龍 ▲1一龍 △1一龍 ▲2一龍 △1一龍 ▲1二飛 *15 △2一龍 ▲1一龍 △1一龍 ▲1一龍 △2一龍 ▲1一龍 △2一龍 ▲2 一龍 △2一龍 ▲1一龍 △1一龍 ▲1一龍 △2一龍 ▲1一龍 △1一龍 ▲2一龍 △1二飛 *16 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △2一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1一龍 △1一龍 ▲1二飛 *17 △1一龍 ▲2一龍 △1一龍 ▲1一龍 △2一龍 ▲2 一龍 △1一龍 ▲2一龍 △2一龍 ▲1一龍 △2一龍 ▲1一龍 △1一龍 ▲2一龍 △1一龍 ▲1一龍 △1 二飛 *18 ▲1一龍 △2一龍 ▲2一龍 △1一龍 ▲2一龍 △1一龍 ▲1一龍 △1一龍 ▲2一龍 △2一龍 (略)
  38. 自己記述集合 THIS IS “THE SELF-DESCRIPTION SET 𝒜” WHERE 𝒜 =

    𝑥, 𝑦 : 𝑥 ∈ ℝ ∧ 𝑦 ∈ ℝ ∧ 𝑥 , 𝑦 ∈ ℬ ∪ 𝒞 ℬ = 𝑥, 𝑦 + 1 : 𝑥 ∈ ℐ 0, 240 ∧ 𝑦 ∈ ℐ 0, 59 ∧ 𝑔 𝒦, 240𝑦 + 𝑥, 2 = 1 𝒞 = 𝑥, −𝑦 − 1 : 𝑥 ∈ ℐ 0, 240 ∧ 𝑦 ∈ ℐ 0, 419 ∧ 𝑓 𝑥, 𝑦 = 1 ℐ 𝑥, 𝑦 = 𝑛 ∈ ℤ ∶ 𝑥 ≤ 𝑛 < 𝑦 𝑓 𝑥, 𝑦 = 𝑔 𝑔 2ℱ, 𝑔 𝒦, 4199 − 60 𝑦 6 − 𝑥 4 , 10 , 1048576 , 4𝑚𝑜𝑑 𝑦, 6 + 𝑚𝑜𝑑 𝑥, 4 , 2 𝑔 𝑘, 𝑥, 𝑏 = 𝑚𝑜𝑑 𝑘𝑏−𝑥 , 𝑏 ℱ = 731024063570851866511465264858993450227206304418681785374039 𝒦 = 1431487833696853176700157431078133300807345625216694848607 028845592243617643316119085595555875582798798158387973177961 695587578188817228313270884309874099454731913241985765682402 57
  39. 自己記述集合の解説 • Quine の考え方と同じ – 上半分(第一象限)はコード B+C • ビットマップデータ 𝒦

    (コード𝐴)をプロットする – 下半分(第四象限)はコード A • コード B+C を表すビットマップデータ 𝒦 を定義する 59
  40. 自己記述集合の解説(上半分) • ビットマップデータ 𝒦 を𝑥軸より上にプロットする • 𝒦: 数式部分のビットマップデータを10進表記したもの • ℐ

    𝑥, 𝑦 = 𝑛 ∈ ℤ ∶ 𝑥 ≤ 𝑛 < 𝑦 – 補助関数、 𝑥 から𝑦の間の整数の集合 • 𝑔 𝑘, 𝑥, 𝑏 = 𝑚𝑜𝑑 𝑘𝑏−𝑥 , 𝑏 – 補助関数、𝑘 を 𝑏 進表記した時の下から 𝑥 番目の桁を取り出す • ℬ = 𝑥, 𝑦 + 1 : 𝑥 ∈ ℐ 0, 240 ∧ 𝑦 ∈ ℐ 0, 59 ∧ 𝑔 𝒦, 240𝑦 + 𝑥, 2 = 1 – 0 ≤ 𝑥 < 240 かつ 0 ≤ 𝑦 < 59 の範囲で、 – 𝑔 𝒦, 240𝑦 + 𝑥, 2 = 1 を満たす 𝑥 と 𝑦 の値 60 自己記述集合
  41. 自己記述集合の解説(下半分) • データ 𝒦 をフォントデータ ℱ で𝑥軸より下にプロットする • ℱ: フォントデータを10進表記した数字(の半分)

    – フォントは横 4 ×縦 5 ピクセル(行間をあけるために4×6として扱う) • 𝑆 = 𝑔 𝒦, 4199 − 60 𝑦 6 − 𝑥 4 , 10 – (x/4, y/6) の位置に書くべき数字を 10 進でとりだす – 4199 は 𝒦 の総桁数、60 は一行あたりに書く桁数 • 𝑇 = 𝑔 2ℱ, 𝑆, 1048576 – その数字のフォントデータを取り出す(1048576 = 24×5 ) • 𝑓 𝑥, 𝑦 = 𝑔 𝑇, 4𝑚𝑜𝑑 𝑦, 6 + 𝑚𝑜𝑑 𝑥, 4 , 2 • 𝒞 = 𝑥, −𝑦 − 1 : 𝑥 ∈ ℐ 0, 240 ∧ 𝑦 ∈ ℐ 0, 419 ∧ 𝑓 𝑥, 𝑦 = 1 – そのフォントデータをビットマップ(2進)に直したもの 61 自己記述集合
  42. 自己記述集合の解説(全体) • 𝒜 = 𝑥, 𝑦 : 𝑥 ∈ ℝ

    ∧ 𝑦 ∈ ℝ ∧ 𝑥 , 𝑦 ∈ ℬ ∪ 𝒞 – ℬ か 𝒞 のいずれかに含まれる 整数組 𝑥, 𝑦 を実数平面にプ ロットする 62 自己記述集合