Slide 1

Slide 1 text

Cookpad Spring 1day Internship 2018 超絶技巧プログラミングコース Yusuke Endoh (@mametter)

Slide 2

Slide 2 text

今日の目的 • 技巧を駆使した実用性のないプログラムを 作成する「超絶技巧プログラミング」を習得する

Slide 3

Slide 3 text

今日のテーマ 超絶技巧 プログラミング 3 Transcendental Programming フランツ・リストのピアノ曲 「超絶技巧練習曲」より借用 Transcendental Ètudes

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

今日のテーマ 超絶技巧 プログラミング =世俗を超越したプログラミング 5 Transcendental Programming 技巧を駆使して 実用性のないプログラムを作成する遊び

Slide 6

Slide 6 text

超絶技巧プログラミングで得られるもの • コンピュータ科学の理論を実践する体験 – ×「才能の無駄遣い」 – 〇「コンピュータ科学 50 年の歴史の無駄遣い」 • 知らない技術に触れるきっかけ • ふつうには出会わない新鮮な制約・価値観 • プログラミング言語の無限の可能性

Slide 7

Slide 7 text

今日の流れ • 午前:講義 – 超絶技巧プログラミングのテクニックを駆け足で学ぶ • 12時~13時:昼食 • 13時~16時:実習 • 16時~:発表会 – 作ったプログラムをお互いに紹介しあう • うまくできたらコンテストに投稿を! – TRICK FINAL(締切:2018/03/31)(遠藤が運営メンバ) – IOCCC25(締切:2018/03/15 03:08:07 UTC) ここで 15 分くらい?

Slide 8

Slide 8 text

講義の流れ • 超絶技巧プログラムの紹介 • 超絶技巧プログラミング入門 • 超絶技巧プログラム事例解説

Slide 9

Slide 9 text

Quine-relay eval$s=%q(eval(%w(B=92.chr;g=32.chr;puts(eval(%q(N=10.chr;n=0;e=->s{Q[Q[s,B],?"].K(N,B+?n)};E=->s{'("'+e[s]+'")'};d=->s,t=?"{s.K(t){t+t}};def~f(s,n)s.K(/.{1,#{n*255}}/m){yield$S=E[$s=$&]}end;Q=->s,t=?${s.K(t){B+$&}};R=";return~0;";V=-> s,a,z{s.K(/(#{B*4})+/){a+"#{$&.size/2}"+z}};C=%w(System.Console~Write);$C=C*?.;$D="program~QR";$G="~contents~of"+$F="~the~mix!g~bowl";$L="public~static";rp=->s,r{v="";[r.!ject(s){|s,j|o={};m=n=0;s.size.times{|i|o[f=s[i,2]]||=0;c=o[f]+= 1;m(p(3+ord~c);Z"-1~0~";c))#{E[%(puts~"#{Q[e[%(echo~'a::=`x7e#{Q[Q["let~s=#{E["!t~mX{Z#{d[E[%(module~QR;!itial~beg!~#{f("let~s=#{ E[%(Module~QR:Sub~MX:Dim~s,n,i,c~As~Object:n=Chr(10):For~Each~c~!"#{d["<#{U="xsl:template"}~match='/'><`x21[CDATA[#{%(sub~f(s$,n)Z(s$);:for~i=1to~n~Z("Y");:next:end~sub:f("#{V[e[%(H,format="#{y="";f("^H{-}{txt}{#{Q["echo~-E~$'#{Q[Q[E[%(with~Ada.Text_Io;procedure~qr~is~beg!~Ada.Text_Io.Put("#{d[% (!terp:library"afnix-sio"`ntrans~O(n){trans~o(afnix:sio:OutputTerm)`no:H(Byte(+~128~n))}`ntrans~f(v~n){`nO(+(/~n~64)107)`nO(n:mod~64)`nO~v}`ntrans~D(n){if(<~n~4){f(+(*~6~n)9)48}{if(n:odd-p){D(-~n~3)`nf~27~48`nf~36~11}{D(/~n~2)`nf~21~48 `nf~48~20}}}`ntrans~S"#{e[%W[STRINGz:=~226+~153,a:=z+~16 6,b:=a+"2 "+z+~160,c:=b+"8"+z+~165,t:="#!clude"+~(10)+"!t"+~(32)+"mX{puts#{d[E[%(class~QR{#$L~void~ma!(SJ[]v){System.out.Z(#{E["H('#{Q[e["implement~ma!0()=Z"+E["BEGIN{ Z#{E[%(echo~'#{%(f(s){System.out.Z(s);}s="389 **6+44 *6+0 0p45*,"; for(c:#{E[(s="#!clude`n!t~mX{std::cout<<#{E[%(class~Program{#$L~void~MX{#$C("Qu!e~Relay~Coffee.^n^nIngredients.^n");for(!t~i=9;i++<126;)#$C($"{i}~ g~caffe!e~{i}^n");#$C("^nMethod.^n");foreac h(c h ar~c~!#{E[%((doseq[s(lazy-cat["IDENTIFICATION~DIVISION.""PROGRAM-ID.~QR.""PROCEDURE~ DIVISION."'DISPLA`x59](map~#(str"~~~~^""(.replace~%1"^""" ^"^"")"^"&")(re-seq~#".{1,45}""#{e["(f=(n )- >Array(n+1).jo!~'Y');console.log('%s',#{V[E[%((H-l!e"#{e["impor t~std.stdio;void~mX{H(`x60#{%(method~MX{Z(@"#{d[" [#{%(class~QR:Application{void~f(SJ~con st~s,!t~n){for(Pr!t(s);n;n--)Pr!t("Y");}void~MX{#{f("IO.puts "+E[%((pr!c~"#{e["`nma!(_)->`nio:fH#{d[E['Zf n("""'+d[?"+"%option~noyywrap`n%%`n%%` n!t~mX{puts#{E["echo~'#{Q[Q[%(~:~A~."#{g*9}"~;~:~ B~A~."~WRITE(*,*)'"~A~;~:~C~B~T`x59PE~." ~'"~CR~;~:~D~S"~#$D"~C~S^"~Z~^"(&"~C~ S^"~#{e[%(Z"#{e["s:=OutputTextUser();Write All(s,#{E[%(Zf"#{e[d[f('set~Z"-";Z'+E [%(package~ma!;import"fmt";func~mX{ fmt.Pr!t#{E[%(236:j;{119:i;{206i-:i;.48<{ 71+}{[i]^48-*}if}%}:t;"algoritmo~Q R;!"[195][173]++'cio~imprima("'"0 1314 1"t"/12131"t~6*"/1:1918151:??62714 13/4=3626612/2 /353251215/`x5a0`x5a0R"t"#{e[%( show~"z=new~java.util.zip.G`x5aI POutp utStream(System.out);z.H('#{ "ma!=putStr"+E["class~QR{#$L ~function~mX{neko.Lib.Z#{E[%(p rocedure~mX;i:=c:=0;s:=#{E[%(.c lass ~public~QR`n.super~#{$T="ja va/io/Pr!tStream"}`n.method~#$L~ma!([L #{S="java/lang/S"}J;)V~;]`n. limit~stack~2`ngetstatic~#{S}ys tem/out~L#$T;`nldc~"#{ e[%(class~QR{#$L~void~ ma!(SJ[]v){SJ~c[]=new~SJ[99999],y="",z=y,s=" #{z=t=(0..r=q=126).map{|n| [n,[]]};a="";b=->n{a<<(n%78+55) %84+ 42};(%(P={0:'[+[]]',m:'((+[])'+(C= "['constructor']" )+"+[])['11']"};for(R~!~B=('`x21[]@`x21`x21[]@[][[ ]]@'+(A="[]['fill']")+"@( []+[])['fontcolor']([])@(+('11e20 ')+[])['split']([])@"+A+C+"('return~escape')()("+ A+')').split( '@'))for(E~!~D=eval(G='('+B[R]+'+[])'))P[T=D[E]]=P[T]| |G+"['"+E+"']";for(G='[ ',B=0;++B<36;)P[D=B.toSJ(36)]= B<10?(G+='+`x21+[]')+']':P[D]||"(+('"+B+"'))['to'+( []+[])"+C+"[ 'name']]('36')";A+=C+"('console.log(unescape(^"";for(E~!~G =#{E[%(A=Z;A("echo~'k` x60");[(A("`x60`x60s"`x5e8* "i");for~j=6:-1:0;x=(Int(c)>>j)%2+1;A("`x60"*"kki"[x:x+1 ])end)fo r~c~!~jo!(["Section`x48eader+name:=QR;SectionPublic-ma!<-(";[ "^"$(replace(replace( s,"Y","YY"),"^"","Y^""))^" .Z;"for~s=matchall(r".{1,99}",#{Q[E["console.log"+Q[E[%(@s=g loba l[#{i=(s=%(`x48AI~1.2`nVISIBLE~"#{"x=sJ.K(#{V[E["changequote(<@,@ >)`ndef!e(p,<@#{"all :`n`t@echo~'#{d["l!el:99 999;Z#{E["solve~satisfy;output~[#{E[%(.assembly~t{}.method~#$L~ vo id~MX{.entrypo!t~ldstr"#{e["m{{`x21:~x`nqr:~|-`n~:db`x60#{e[s="$Z#{E[%(#!clude< stdio.h>`nmodule~QR{ }implementation{!t~mX_ _attribute__((C,spontaneous)){puts#{E["Zf#{E["echo"+E["#import#{ N}!t~mX{puts#{E["Z_sJ"+E["s=toascii#{E["Z#{E["$console:l!e[#{"# $D(output);beg!~H(# {f((p="eval";%($_="#{ s,v=rp["$_='#{Q[%($z)&&$i/$z<($c<$w?ord($s[(!t)( $c/3)]):$c--%3+2)? $t[2].$t[$c%3%2].$ t[$c%3]:"^0^0^0":"^0")$c=$i%$z;foreach(array("I`x48DR".pack("NNCV",$w+2,128,8,2 ),"IDAT".gzcompress($m),"IEND")as$ d)echo~pack("NA*N ",strlen($d)-4,$d, crc32($d));).K(B,"`x7f"),?']}';s:g/^x7f/Y/;Z~$_",128..287];s="$_='#{Q[s,c=/['Y]/ ]}';$n=32;$s='#{Q[v,c]}';$s=`x7 es{..}{$a=$&;$b=c hr(--$n&255);`x7e s/$b/$a/g;}eg;Z";(s+N*(-s.size%6)).unpack("B*")[0].K(/.{6}/){n=$&.to_i~2;((n+14)/2 6*6+n+47).chr}}";s|.|$n=ord $&;substr~unpack( B8,chr$n-!t($n/32 )*6-41),2|eg;eval~pack'B*',$_).scan(/[~,-:A-z]+|(.)/){p="s++#{$1?"chr~#{$1.ord}+e": $&+?+};"+p};p),1){"'#$s' ,"}}'')end.".K(/[: ;()]/){?`x5e+$&}} ]"]};quit"]};t=num2cell(b=11-ceil(s/13));for~n=1:9m={};for~i=1:141f=@(x,y,n)repmat ( ['Ook'~char(x)~'~Ook' ~char(y)~'~'],[1~a bs(n)]);m(i)=[f(z =46,63,n)~f(q=z-(i<13)*13,q,i-13)~f(33,z,1)~f(63,z,n)];end;t(x)=m(diff([0~s(x=b= =n )])+13);en d;Zf ('%%s',t{:})"]]+R} }"]]}`n"]};return~ 0;}~})]};"]}`x60` n~global~_start`n~_start:mov~edx,#{s.size}`n~mov~ecx,m`n~mov~ebx,1`n~mov~eax,4` n ~!t~128`n~mov ~ebx,0`n~mo v~eax,1`n~!t~12 8`nx:~|`n~}}{{{qr}} }"]}"call~void~[ms corlib]#{C*"::"}(sJ)ret})]}];"]};quit();",?$].K(?'){"'^''"}}'"}@>)`np"],?&,?& ] },'&(%d+)&',fu nction(s)retur n~sJ. rep('Y',tonu mber(s))end);Z(x)". K(/[:"]/,":^0")}"` n`x4bT`x48`x58B`x59E~B`x59E)).size+1}x~i8]c"#{s.K(/[^"`n`t]/){"^%02`x58"%$ &. ord}}^00"declare~i32@puts(i8*)d ef!e~i32@mX{ %1=call~i 32@puts(i8*getelemen tptr([#{i}x~i8],[#{ i}x~i8]*@s,i32~0,i32~0))ret~i32~0})],?#]]]})];");"],"^n")];[for~i=0:2:4; x=(( Int(c)%83-10)>>i)%4+1;A("ski`x60 "[x:x])end~for~c ~!"AG- `x48-`x48Fy.IlD==;=jd lAy=;=jldltldltl{lAu lAy=jtlldlAyFy=?=jdlAyGFyFyG2AFy>zlAFFBCjldGyGFy>GFy.AGy=G==n`x48==nl ldC=j@=j tlldltldlAut11"];A("'"))]})A+="' +`x21[]+'"+G.charCo deA t(E).toSJ(16);for(A+=" ^".replace(/'+`x21[] +'/g,^"%^")))')()",R=0;R<9;R++)A=A.replace(/'.*?'/g,function(B){T= [];for(E=1 ;B[E+1];)T.push(P[B[E++]]);return~T.jo!('+')});conso l e.log('"'+A+'"'))).byte s{|n|r,z=z[n]||(b[r/7 8];b[r];q<6083&&z[n]=[q+=1,[]];t[n])};b[r/78];b[r]}";!t~i=0,n=0 ,q=0;for(;++n< 126;)c[n]=""+(char)n;for(;i<#{a.size};){q=q*78+(s .charAt(i)-13)%84;if(i++ %2>0){y=qn{L<<(n+62) %92+35;D};s.bytes{|c|n>0?n-=1:(t[c]=(t[c]||[]).reject{|j| j4&&x<<[k,j]};x=x. max)?(n,j=x;x=b.size;(u=[x,3999].m!;D[u%87][u/87];L<0;x=4001+i-j;D[x%87][x/87][n-5] ):b<`nchar*p=#{E [L]},s[999999],*q=s;!t~mX{!t~n,m;for(;*p;){n=(*p-5)%92+(p[1]-5)%92*87;p+=2;if(n >3999)for(m=(*p++-5)%92+6;m--;q++)*q=q[4000-n];else~for( ;n--;)*q++=*p++;}puts(s)#{R}}")]}){s+="00g,";for(m=1;m<256;m*=2)s+="00g,4,:"+(c/m%2>0?"4+":"")+",";f(s);s="4,:,";}f(s+s);for(c:Base64.getDecoder().decode("kaARERE`x58/I0ALn3n5ef6l/Pz8+fnz58/BOf5/7/hE`x58/O`x5azM5mC`x58/Oczm`x5azBPn5+`x 58/OczMznBL/nM5m`x5azBPu++fPPOc5zngnnO`x5azO`x5agnBMGAW7A==")){c=c<0?256+c:c;for(i=0;i++<3;c/=8)f(c%8);f("8*+8*+,");}f("@");).K(?',%('"'"'))}'|sed~-e's/Y/YY/g'~-e's/"/Yq/g'~-e's/.*/Z~"&"^nquit/')]}}"]],?']}');".K(B,?`x5e)]}.replace("`x 5e","Y"));}})]]};}";FORiTO`~UPBtDO`~INTn:=ABSt[i];Z(~(50+n%64)+c+~(50+n%8MOD8)+c+~(50+nMOD8)+b+"`x4a"+a)OD]*"REPR"]}"`ntrans~c~0`ndo{D(Integer(S:get~c))`nf~35~39}(<(c:++)(S:length))`nf~24~149)].K(N,'"&Character'+?'+'Val(10)&"')}");end; )]+"`nsys.exit~0",B],?']}'",/[^{}]/]}}",35){y<<",`n"+$S;"%s"}}")+y],'",','):f("']}",0))}]]>#{U}>"].K~N,'"&~VbLf~&"'}":s="~~~":For~i=0To~7:s~&=Chr(32-(Asc(c)>>7-i~And~1)*23):Next:#$C(s~&n~&Chr(9)&n~&"~~"):Next:#$C(n~&n~&n): End~Sub:End~Module)]}`nput=s`nZ`nqa`x21",3){%($H("%s",#$S);)+N}}end~endmodule)],?%]+R}}"]},i=0,t='k';while(s[i])t='^x60.'+s[i++]+t;console.log(t)",B],?`x21].K(?',%('"'"'))}'"^n::=^na")],/[`[`]$]/]}")]};Z"0~0~-1";)],?']}';cr",127..255]; f(%(variable~s=`x60#{s.K(/.{1,234}/){$&.K("`x60",%(`x60+"`x60"+`x60))+"`x60+`n`x60"}}`x60,i;for(i=0;i<129;i++)s=strreplace(s,pack("C",255-i),substrbytes(`x60#{v[0,99]}`x60+`n`x60#{v[99..-1]}`x60,i*2+1,2));Zf("%s",s)),7){"f('%s')`n"%$s. unpack("`x48*")}}Zf("^n#[Exeunt]");quit)]}")),196){%(Z#$S;)}}}"]});})).gsub(/[!HJKXYZ^`~]/){[B*2,:write,B,:tring,:gsub,"ain()",B*4,:print,g,:in][$&.ord%47%12]})))*"")#_buffer_for_future_bug_fixes_#_buffer_for_future_bug_fixes_#_buffer_ ############################################################################## Quine Relay -- Copyright (c) 2013, 2014 Yusuke Endoh (@mametter), @hirekoke #############################################################################)

Slide 10

Slide 10 text

Quine-relay • A Ruby code • that generates Rust code • that generates Scala code • … • that generates REXX code • that generates the original Ruby code 128 languages involved in total

Slide 11

Slide 11 text

Quine-relay • A Ruby code • that generates Rust code • that generates Scala code • … • that generates REXX code • that generates the original Ruby code 128 languages involved in total

Slide 12

Slide 12 text

Merry Quine-mas https://youtu.be/r0eaf9iLKxg

Slide 13

Slide 13 text

Merry Quine-mas https://youtu.be/6xALa5RiKUE

Slide 14

Slide 14 text

14

Slide 15

Slide 15 text

15

Slide 16

Slide 16 text

mquine.rb 16 eval(_=%[b='DEILMQTVY';eval((%[a=(-1)EE0.5;f=->EfVf.each_slice(2)Y;c=->wVz=->dVd.mapVd=d.rotate(1)YY ;Q=->k,l,mV((m-k)E(l-k).conT).arQ<0Y;y=[];x=0.99;o,T=w.partitionV|n|d=0;z[n].mapV|k,l|y<rVk,m,l=r;k=k[2],l,m[2];r[I]=Q[Ek]DDv.allMV|q|z[k].anyMV|k,l|q==k||Q[k,q,l]YYY;z[s].mapV| k,l,m|l[0,2]=k,m;n[l]Y;(s[I..-1].mapVt=t[1]until(t[I]);k,m=l=t;m[0],k[1]=t;n[m];n[t=k];lY<+TIEduE45u>mv%^Y=Vny-`zce)k`heIt%`Vzf;c2nk4d|Vp^D_,,|kDDL2r_sDy%%fiMV6cYE)5`,m/k YQ/;IEezMVv,QchILY|p%%i=4Q2(vwael61//-l67uQ;2Tq,c_'qEIcm1cL;i++2-tYdbenq%pxr;2'Vn (uDa)n)zf4w)%5vh;ssV5kI;)z;2=+ToeIeT9-(iM1s/mYQvEY`vMxu%Y0u5 7Qaoh2Ep+icop_u0%=-zv,omq`Qz/41DV'`f9L5`M'bVxx%/;qLtq12%q:V'9,fD,ovu%qr|+e+rudI`Ir0 5chVd+Q5`II76rY0laT%b(I>Y%EQ1xf^|r,1)%4--zQa'c5Dmd: 9-I15%Lp/>>z57^,Th2>%la0;5`dE1qy;f6)||Ikw_0wdMM5so7b..4qy`nQrz%Vf7Ii^epY(x=|49Lh(=>sI_sbofb7|qM,unaD%^i:|;_tEEnb-DDt`t%I2h;0x5f^yhs,pbLf+m^e>yqzQ '%::|^=,5-b=^_1x1se`kp,%wq4T%;'E.:Dsp_V-0||,)=;.a|<%0QY:;t:fEmk:4|_%o-.:aooq/6mThdvz4`uQqY1r'em.5'z1 2p7e%%pp6ebMM,m`5QpYx'd`,`6a4T)6Q.k.E.YsdiE^ox9pyrsr%|(kfn=y9q`6;=V%iie%)y-1i(y 'V-n^uTva%l0Q>,yz;E0:LbV'eTb6MIb``Da.__ihbacxY|fc6>pTtl;ivVt,q>/%w,=hnI+i90>10u59te,Ildw4p94x`iwvs`f +^)w1M>%wf^].bytesV|i|e=eE59+(i-5)%L9Y;Q=->iVk=e%i;e/=i;kY;d=VI2=>c[[]]Y;54.upto(1I0)V|h|d[h%L9+I7]= c[(0..Q[5]).mapVl=o=T=[];n=0;(-2..Q[17]).mapV[l=Q[2],o=Q[1I]+Q[21]Ea+1+a]Y.flat_mapV|m,n|E,(h,)=[[o, l],[(o+o=n)/2,0]][0..lDl=m]Y.mapV|o,l|n=l<1M((n==0ML:1).upto(L)V|k|T<fiz|'.tr('x%-|','%-'<<125)));' [[[ Monumental Quine (c) 2015 Yusuke Endoh -- tested with ruby 2.2.1 -- built on 2015/04/01 ]]]'])

Slide 17

Slide 17 text

実行 17 $ ruby mquine.rb > mquine.obj

Slide 18

Slide 18 text

mquine.rb: Monumental Quine 自己言及的記念碑 自分自身の作り方を Ruby プログラムとして刻んだ円柱 ※Quine:自分自身を出力するプログラムのこと 18 Ruby実行 3Dプリント

Slide 19

Slide 19 text

rquine.rb : 宇宙線耐性 Quine 19 eval=eval=eval='eval$s=%q(eval(%w(puts((%q(eval=ev al=eval=^Z^##^_/#{eval@eval@if@eval)+?@*10+%(.size >#{(s=%(eval$s=%q(#$s)#)).size-1}}}#LMNOPQRS_##thx .flagitious!## )+?@*12+%(TUVW XY/.i@rescue## /_3141592653 589793+)+?@* 16+%(+271828 182845904; _987654321 0;;eval)+? @*18+%("x =((#{s.s um}-eval. _sum)%256 ).chr; ;eval)+?@ *12+%(.s can(//){ a=$`+x+$ ^_a.unpa ck (^ H*^)[0]. hex%999989==#{s.unpac k("H*")[0].hex%999989 }&&eval(a)}#"##"_eval @eval####@(C)@Copyrig ht@2014@Yusuke@Endoh@# ###)).tr("@_^",32.chr< <10<<39).sub(?Z,s));e xit#AB CDEFGHIJK)*%()))#'##' /#{eval eval if eval .size>692}}#LMNOPQRS ##thx.flagitious!## TUVWXY/.i rescue##/ 3141592653589793+ +271828182845904; 9876543210;;eval "x=((42737-eval. sum)%256).chr;;eval .scan(//){a=$`+x+$' a.unpack('H*')[0].hex%999989==68042&&eval(a)}#"##" eval eval#### (C) Copyright 2014 Yusuke Endoh ####

Slide 20

Slide 20 text

実行 (1) 20 $ ruby rquine.rb > rquine2.rb $ diff -s rquine.rb rquine2.rb ファイル rquine.rb と rquine2.rb は同一です

Slide 21

Slide 21 text

broken.rb (適当に1文字消した) 21 val=eval=eval='eval$s=%q(eval(%w(puts((%q(eval=ev al=eval=^Z^##^_/#{eval@eval@if@eval)+?@*10+%(.size >#{(s=%(eval$s=%q(#$s)#)).size-1}}}#LMNOPQRS_##thx .flagitious!## )+?@*12+%(TUVW XY/.i@rescue## /_3141592653 589793+)+?@* 16+%(+271828 182845904; _987654321 0;;eval)+? @*18+%("x =((#{s.s um}-eval. _sum)%256 ).chr; ;eval)+?@ *12+%(.s can(//){ a=$`+x+$ ^_a.unpa ck (^ H*^)[0]. hex%999989==#{s.unpac k("H*")[0].hex%999989 }&&eval(a)}#"##"_eval @eval####@(C)@Copyrig ht@2014@Yusuke@Endoh@# ###)).tr("@_^",32.chr< <10<<39).sub(?Z,s));e xit#AB CDEFGHIJK)*%()))#'##' /#{eval eval if eval .size>692}}#LMNOPQRS ##thx.flagitious!## TUVWXY/.i rescue##/ 3141592653589793+ +271828182845904; 9876543210;;eval "x=((42737-eval. sum)%256).chr;;eval .scan(//){a=$`+x+$' a.unpack('H*')[0].hex%999989==68042&&eval(a)}#"##" eval eval#### (C) Copyright 2014 Yusuke Endoh #### e

Slide 22

Slide 22 text

実行 (2) 22 $ ruby broken.rb > rquine2.rb $ diff rquine.rb rquine2.rb ファイル rquine.rb と rquine2.rb は同一です どの 1 文字を消しても元に戻ります 壊れているのに元のプログラムを出力した!

Slide 23

Slide 23 text

講義の流れ • 超絶技巧プログラムの紹介 • 超絶技巧プログラミング入門 • 超絶技巧プログラム事例解説 ここで 40 分くらい?

Slide 24

Slide 24 text

今日紹介するテクニック • プログラムのアスキーアート化 • ターミナルでアニメ • Quine • その他のテクニック

Slide 25

Slide 25 text

プログラムのアスキーアート化 • 好きなプログラムを書く – 空白は使わない – 改行も使わない – ¥ も使わない • おまじないで囲む p 42 p(42) p(1) p(2) p(1);p(2) puts("¥¥") puts(92.chr) eval(%w( puts("Hello,world!") ).join) この範囲を自由に整形可能

Slide 26

Slide 26 text

プログラムのアスキーアート化 • 好きなプログラムを書く – 空白は使わない – 改行も使わない – ¥ も使わない • おまじないで囲む p 42 p(42) p(1) p(2) p(1);p(2) puts("¥¥") puts(92.chr) eval(%w( puts("Hello,world!") ).join) この範囲を自由に整形可能 eval(%w( p u t s ( " H e l l o , w o r l d ! " ) ).join)

Slide 27

Slide 27 text

解説 • %w(foo bar) は 文字列の配列 ["foo", "bar"] を返す • Array#join は文字列を結合して返す – 空白や改行が取り除かれる • eval は文字列を Ruby コードとして実行する eval(%w( p ( 1 ) ).join) #=> 1 が出力される %w( p ( 1 ) ).join #=> "p(1)" %w( p ( 1 ) ) #=> ["p","(","1",")"]

Slide 28

Slide 28 text

実習(10分) • 自己紹介を出力するプログラムを イニシャル形状にしてみましょう – 元プログラム↓ – 余分は # や ; などで埋める – 自己紹介やイニシャルではなく、好きなメッセージや 形状でも構いません eval(%w( puts("Yusuke-Endoh,hobby:walking") ).join) eval(%w( pu ts ("Yusu Ke -E nd oh,h obby:w al ki ng ") ###### ## ).join) eval(%w(puts("YusuKe -E nd oh,h ob By :w alki ng")## ### ##### ## #### ###### ###### #### ## ## ## #############).join)

Slide 29

Slide 29 text

実習(発展問題) • ¥ を含めるとどのような問題が起きるか、 実際に試して調べてみましょう • これ↑はなぜ動かない? • 発展:¥ の避け方を考えてみましょう puts("foo¥nbar") p("foo1bar2baz".gsub(/¥d/, ",")) p("foobar foo bar".gsub(/¥bfoo¥b/, "FOO")) eval(%w(puts("¥¥")).join)

Slide 30

Slide 30 text

発展問題の解答例 • ¥ は %w(…) がエスケープとして消費する – %w(puts("¥¥")).join は puts("¥") になる – 宿題:%w(puts("¥¥¥¥")).joinにすればOK? • "foo¥nbar" は "foo#{ 10.chr }bar" と 書き換えられる – 各文字には、対応する数字(codepoint)がある • 調べ方→ – 他の書き換えは宿題 p "¥n".ord #=> 10 p 10.chr #=> "¥n" ここで 1 時間くらい?

Slide 31

Slide 31 text

ターミナルで色 • エスケープシーケンスを print することで ターミナルを制御できる – 色を変えたり、カーソルを移動したり • "¥e[数字m" で色を変えられる – 色以外にもいろんな属性がある (ANSI エスケープシーケンスで検索) puts "¥e[34mfoo¥e[0m" #=> foo(青字) puts "¥e[44mfoo¥e[0m" #=> foo(青地) 30黒 31赤 32緑 33黄 34青 35紫 36水 37白 40黒 41赤 42緑 43黄 44青 45紫 46水 47白

Slide 32

Slide 32 text

ターミナルでアニメ • アニメの基本:繰り返し端末に print する – アスキーアートを見せる→少し待つ →アスキーアートを見せる→少し待つ→…の繰り返し – print と sleep を繰り返すだけでも良いが エスケープシーケンスを使うとより見やすくなる • アニメに役立つエスケープシーケンス – "¥e[H" でカーソルをホームに移動 – "¥e[J" で画面の文字を全消去 • 正確には、カーソル位置より後にある文字を消去

Slide 33

Slide 33 text

アニメの例 • 最初にターミナルをクリアする • ホームに戻って puts – を繰り返す – ポイント:画面のクリアは最初だけ (毎回やるとちらつく) print "¥e[H¥e[J" loop do print "¥e[H" puts "<--+" puts " |" puts " " sleep 0.5 print "¥e[H" puts "+-- " puts "| " puts "V " sleep 0.5 print "¥e[H" puts " " puts "| " puts "+-->" sleep 0.5 print "¥e[H" puts " ^" puts " |" puts " --+" sleep 0.5 end <--+ | +-- | V | +--> ^ | --+

Slide 34

Slide 34 text

実習(10分) • 波々なアニメプログラムを書いてみましょう – 土台→ • 波に色を つけて みましょう • 発展:このプログラムをアスキーアート化してみましょう – ヒント:"¥e" を避ける必要がある W, H = 60, 20 def print_sine_curve(phase) s = (0..H).map { " " * W } W.times do |x| y = Math.sin((phase+x)/W.to_f*2*Math::PI) s[((y + 1) * H / 2).round][x] = "o" end puts s.join("¥n") end print_sine_curve(0) # <= ここの数字を変えてみる

Slide 35

Slide 35 text

解答例 • 波々アニメプログラム→ • 波に色をつける↓ • 発展問題は宿題("¥e" は 27.chr) phase = 0.0 print "¥e[H¥e[J" loop do print "¥e[H" print_sine_curve(phase) phase += 1.5 sleep 0.1 end def print_sine_curve(phase) s = (0..H).map { " " * W } (W - 1).downto(0) do |x| # x を逆順に回す y = Math.sin((phase+x)/W.to_f*2*Math::PI) s[((y + 1) * H / 2).round][x] = "¥e[41mo¥e[0m" end puts s.join("¥n") end

Slide 36

Slide 36 text

Quine • 自分自身を出力するプログラム – Quine の名前の由来:間接自己言及を発見した 哲学者 Willard Van Orman Quine # Quine を実行して、出力を別ファイルに保存する $ ruby quine.rb > quine2.rb # 出力と元プログラムを比較する $ diff –s quine.rb quine2.rb ファイル quine.rb と quine2.rb は同一です

Slide 37

Slide 37 text

よくある失敗 • 「何かを出力するプログラム」からQuineを目指す • 終わりがない puts "..." puts "puts ¥"...¥"" puts "puts ¥"puts ¥¥¥"...¥¥¥"¥""

Slide 38

Slide 38 text

うまくいく方針 • 出力文字列を変数に代入しておく • 埋め込みをプログラムにやらせる s="..."; puts s s="s=¥"…¥"; puts s"; puts s s = s.sub("…", s)

Slide 39

Slide 39 text

うまくいく方針 • まとめると • 出力結果 – ほぼ一致、¥ がないだけ s="s=¥"...¥"; s=s.sub(¥"...¥"); puts s"; s=s.sub("...", s); puts s s="s="..."; s=s.sub("..."); puts s"; s=s.sub("..."); puts s ※紙面の都合で改行を入れています

Slide 40

Slide 40 text

¥ の対処方法 • ¥ を使わずに書く – ¥ が必要な理由は、 "…" の中に " を入れるため – ならば、" を使わなければよい • %[…] という文字列リテラルを使う – ¥ なしで区切り文字自身を含められるのがポイント %[foo bar] #=> "foo bar" %[foo [bar] baz] #=> "foo [bar] baz"

Slide 41

Slide 41 text

動作する Quine • 書き直したプログラム • 出力結果 – 完全一致! s=%[s=%[…]; s=s.sub(%[…], s); puts s]; s=s.sub(%[…], s); puts s s=%[s=%[…]; s=s.sub(%[…], s); puts s]; s=s.sub(%[…], s); puts s

Slide 42

Slide 42 text

実習(15 分) • Quine を入力して実際に動かしてみましょう • もう少し短い Quine にしてみましょう – s=s.sub(%[…], s); puts s をまとめて puts s.sub(%[…], s) とするとか – 全部の "…" を %[…] に置き換えましたが、一部は "…" のままにできます • 発展:¥ から逃げずにQuineを書いてみましょう – ヒント:s の中の " をすべて ¥" に置換すればよい

Slide 43

Slide 43 text

解答 • 短くする – 文字列の中と外で 2 回変更することがポイント • 発展問題のヒント:???? を埋める – ¥ を置換するからと言って ¥ を使うとハマるので… s=%[s=%[…];puts s.sub("…",s)]; puts s.sub("…",s) s="s=¥"…¥"; puts s.sub(¥"…¥", s).gsub(????){????}"; puts s.sub("…", s).gsub(????){????}

Slide 44

Slide 44 text

もっとかんたんな Quine • いちいち 2 箇所変えないと行けないのはダルい  eval を使えばもっとシンプルに書ける • 動作の仕組みを実習時間に考えてみてください – ポイント:%[…] と %q[…] の違い eval(s=%q[puts %[eval(s=%q[#{ s }])]]) %[...#{ 42 }...] #=> "...42..." %q[...#{ 42 }...] #=> "...#{ 42 }..." ここで 1 時間 40 分くらい?

Slide 45

Slide 45 text

その他のテクニック • 画像・動画生成 – 頑張ってデータを生成して出力する – 書籍の8.3節を参照のこと • サウンド – 音声の波形データを生成して、どうにか再生する – 書籍の8.2節を参照のこと • プログラムの短縮 – 次ページ以降で解説

Slide 46

Slide 46 text

コードゴルフ • プログラムの短さで競う競技 – "Anarchy golf" で検索 • 超絶技巧プログラミングは短さを競うわけではな いが、同じ挙動なら短いほうが大体エレガント – TRICK 等のサイズ制限を抜けるために必要なことも • 今回はRubyに特化した細かいテクニックではなく、 データの圧縮アルゴリズムを紹介します

Slide 47

Slide 47 text

超絶技巧プログラミングと圧縮 • 超絶技巧プログラムにデータを持たせたいケース – 形状データ、フォントデータ、楽譜データ、etc. • 基本的なアプローチ – 元データと圧縮コードを書いて圧縮データを作る – 圧縮データと展開コードを最終的なプログラムに入れ る • 圧縮データは(通常は)文字列として埋め込む • 展開コードは Ruby で書いて埋め込む • 圧縮コードは最終的なプログラムに埋め込む必要がない

Slide 48

Slide 48 text

トレードオフ • 単純な圧縮・展開アルゴリズム – 圧縮データは長い(低圧縮率) – 展開コードは短い • 頭のいい圧縮・展開アルゴリズム – 圧縮データは短い(高圧縮率) – 展開コードは長い • なんとなくの目安 – データが30バイト程度なら無圧縮 – 100 バイト程度なら 36 進数 – 1000 バイト程度なら Byte-pair Encoding

Slide 49

Slide 49 text

36進数の例 ● 元データ (横 3 × 縦 5 × 文字数 11 = 165 バイト) ● 空白を 0 、# を 1 にして二進数に ● 10 進表示に (50 バイト) – 1073733623, 2905756245, 1063256021, 2371130133, 1063231487 ● to_s(36)で36進数にすればさらに小さく(35バイト) – 0hr9u5z, 1c20fv9, 0hl19lh, 137pjpx, 0hl0qnz ### ## ### ### # # ### ### ### ### ### # # # # # # # # # # # # # # # # # ### ### ### ### ### # ### ### # # # # # # # # # # # # # ### ### ### ### # ### ### # ### ### 111 110 111 111 101 111 111 111 111 111 101 010 001 001 101 100 100 001 101 101 101 010 111 111 111 111 111 010 111 111 101 010 100 001 001 001 101 010 101 001 111 111 111 111 001 111 111 010 111 111

Slide 50

Slide 50 text

BPE (Byte-pair Encoding) (1) • 繰り返しの多いデータで効く単純な圧縮 • 圧縮方法 – 元データのうち出現頻度の高い 2 連続文字を見つける • "0000100000100001" の場合、"00" の出現頻度が最大 – その 2 連続文字を、未使用文字に置き換える • "0000100000100001"  "2212201221" – 利用した文字と 2 連続文字を先頭にくっつけておく • "2" + "00" + "2212201221"` – これを繰り返す • "2002212201221"  "3"+"22"+"2003130131"`

Slide 51

Slide 51 text

BPE (Byte-pair Encoding) (2) • 展開方法 – 圧縮データ "3222003130131" – 先頭文字と続く 2 文字を取り出す • "3" と "22" – "3" を "22" に置換する • "2003130131".gsub("3", "22") #=> "2002212201221"` – これを繰り返す • "2212201221".gsub("2", "00") #=> "0000100000100001"`

Slide 52

Slide 52 text

さまざまな圧縮アルゴリズム • 超絶技巧プログラミングでは BPE のコストパフォーマンスが最強な場合が多い – 簡単、簡単な割に高圧縮、展開コードも短い、形 状・フォントデータ(繰り返しが多い)と相性がよい • 圧縮アルゴリズムは他にも色々あります – 定番はランレングス – LZ77、LZ78、Range Coder 、算術符号など • GIF 生成では LZ78 の亜種 LZW の知識があるとよい – 興味がある人は調べて見てください

Slide 53

Slide 53 text

講義の流れ • 超絶技巧プログラムの紹介 • 超絶技巧プログラミング入門 • 超絶技巧プログラム事例解説

Slide 54

Slide 54 text

Quine-relay の解説 • 理論的には、Ruby の Quine そのもの – 自分自身をそのまま出力するのではなく、 その文字列を出力する別言語プログラム、 にラップして出力する – このラッピングを 100+ 言語分繰り返すだけ – 午後の実習ネタにあるので、考えてみてください

Slide 55

Slide 55 text

Merry Quine-mas の解説 • 本質的には Ruby の Quine そのもの – 自分自身をそのまま出力する前に、 アニメーションと音楽演奏を行う – アニメーションの方法はまさに講義で説明した通り – コードを 80x25 = 2000 文字に収めるのは 種々の圧縮テクニックが必要(収めなくてもよいけど)

Slide 56

Slide 56 text

Monumental Quine の解説 – mquine.rb – 縁遠かった技術や、ふつうには出会わない制約を 体験できる 56 eval(_=%[b='DEILMQTVY';eval((%[a=(-1)EE0.5;f=->EfVf.each_slice(2)Y;c=->wVz=->dVd.mapVd=d.rotate(1)YY ;Q=->k,l,mV((m-k)E(l-k).conT).arQ<0Y;y=[];x=0.99;o,T=w.partitionV|n|d=0;z[n].mapV|k,l|y<rVk,m,l=r;k=k[2],l,m[2];r[I]=Q[Ek]DDv.allMV|q|z[k].anyMV|k,l|q==k||Q[k,q,l]YYY;z[s].mapV| k,l,m|l[0,2]=k,m;n[l]Y;(s[I..-1].mapVt=t[1]until(t[I]);k,m=l=t;m[0],k[1]=t;n[m];n[t=k];lY<+TIEduE45u>mv%^Y=Vny-`zce)k`heIt%`Vzf;c2nk4d|Vp^D_,,|kDDL2r_sDy%%fiMV6cYE)5`,m/k YQ/;IEezMVv,QchILY|p%%i=4Q2(vwael61//-l67uQ;2Tq,c_'qEIcm1cL;i++2-tYdbenq%pxr;2'Vn (uDa)n)zf4w)%5vh;ssV5kI;)z;2=+ToeIeT9-(iM1s/mYQvEY`vMxu%Y0u5 7Qaoh2Ep+icop_u0%=-zv,omq`Qz/41DV'`f9L5`M'bVxx%/;qLtq12%q:V'9,fD,ovu%qr|+e+rudI`Ir0 5chVd+Q5`II76rY0laT%b(I>Y%EQ1xf^|r,1)%4--zQa'c5Dmd: 9-I15%Lp/>>z57^,Th2>%la0;5`dE1qy;f6)||Ikw_0wdMM5so7b..4qy`nQrz%Vf7Ii^epY(x=|49Lh(=>sI_sbofb7|qM,unaD%^i:|;_tEEnb-DDt`t%I2h;0x5f^yhs,pbLf+m^e>yqzQ '%::|^=,5-b=^_1x1se`kp,%wq4T%;'E.:Dsp_V-0||,)=;.a|<%0QY:;t:fEmk:4|_%o-.:aooq/6mThdvz4`uQqY1r'em.5'z1 2p7e%%pp6ebMM,m`5QpYx'd`,`6a4T)6Q.k.E.YsdiE^ox9pyrsr%|(kfn=y9q`6;=V%iie%)y-1i(y 'V-n^uTva%l0Q>,yz;E0:LbV'eTb6MIb``Da.__ihbacxY|fc6>pTtl;ivVt,q>/%w,=hnI+i90>10u59te,Ildw4p94x`iwvs`f +^)w1M>%wf^].bytesV|i|e=eE59+(i-5)%L9Y;Q=->iVk=e%i;e/=i;kY;d=VI2=>c[[]]Y;54.upto(1I0)V|h|d[h%L9+I7]= c[(0..Q[5]).mapVl=o=T=[];n=0;(-2..Q[17]).mapV[l=Q[2],o=Q[1I]+Q[21]Ea+1+a]Y.flat_mapV|m,n|E,(h,)=[[o, l],[(o+o=n)/2,0]][0..lDl=m]Y.mapV|o,l|n=l<1M((n==0ML:1).upto(L)V|k|T<fiz|'.tr('x%-|','%-'<<125)));' [[[ Monumental Quine (c) 2015 Yusuke Endoh -- tested with ruby 2.2.1 -- built on 2015/04/01 ]]]']) TrueTypeフォントデータの テキスト圧縮表現+レンダリング 穴ありポリゴンの凸多角形分割 3Dプリント可能なモデルデータ生成 コードを短くする (世俗的)インセンティブ 3Dプリンタ代:1行あたり約$15! 複雑な字形を使わない “3”, “8”, “g” などを一切不使用

Slide 57

Slide 57 text

放射線耐性 Quine の解説 • 文字列 x と y が一致したとき どちらも破壊されていない  好きな方を eval すればよい • 文字列 x と y が異なるとき 長い方は破壊されていない  長い方を eval すればよい 57 x = ”…プログラム…” y = ”…プログラム…” eval x if x == y eval [x, y].max_by {|s| s.size } 次の疑問 : 文字列の外が破壊されたら? たとえば、ここのダブルクォートが消えたら?

Slide 58

Slide 58 text

プログラムの宇宙線耐性化 (1) • Ruby の文法を研究・試行錯誤し、 どの文字を消しても意味が変わらない書き方を 発見した 58 x = ”…プログラム…” eval=eval=eval=”(プログラム;exit)#”##” 書き直し

Slide 59

Slide 59 text

プログラムの宇宙線耐性化 (2) 59 eval=eval=eval=”(プログラム;exit)#”##” eval=eval=eval”(プログラム;exit)#”##” イコールが削除された メソッド呼び出しに化ける (Rubyではメソッド呼び出しの括弧を省略可) これらの eval はメソッドではなく変数 (Rubyでは同名の変数とメソッドが共存できる)

Slide 60

Slide 60 text

プログラムの宇宙線耐性化 (3) eval=eval=eval=”(プログラム;exit)#”##” eval=eval=eval=(プログラム;exit)#”##” 文字列の中身が普通に実行される (Rubyでは式と文の区別があまり ない) ダブルクォートが削除された 文字列終端のクォートは コメント化される

Slide 61

Slide 61 text

プログラムの宇宙線耐性化 (4) 61 eval=eval=eval=”(プログラム;exit)#”##” eval=eval=eval=”(プログラム;exit)###” コメントの中のダブルクォートが新しい文字列終端になる ダブルクォートが削除された ✓ 他のどの文字が消されても、いい感じに動く ✓ プログラム全体にわたって同じような調整を施した ✓ 詳細は書籍の第 7 章を参照してください

Slide 62

Slide 62 text

講義はここまで

Slide 63

Slide 63 text

実習(~16時くらいまで) • 面白いと思うプログラムを自由に書いてください – 講義内容を参考してもしなくても – ネタを思いつかない人は次ページ以降の案を参考に – 質問は随時どうぞ • 16 時頃から各自作ったもののデモと説明をして 頂きます – 改良して TRICK などに応募するかもしれない人は 申し出てください

Slide 64

Slide 64 text

ネタ案 • 色のついたプログラム – $ cat prog.rb としたら、一部に色がついている – $ ruby prog.rb としたら、何か動作する – ヒント1:puts "Hello" の H を赤くしてみるとか – ヒント2:プログラムを出力する プログラムを書くと簡単 • 色のついた Quine puts <

Slide 65

Slide 65 text

ネタ案 • 自分自身を難読化して出力する Quine – 難読化は自由、簡単にはBASE64やROT13 • エンコードの方法 • コマンドラインでデコードする例 • ruby prog.rb | base64 –d で自分自身が出たらOK require "base64" puts Base64.encode64("foo") #=> Zm9v $ echo Zm9v | base64 –d foo

Slide 66

Slide 66 text

ネタ案 • 自分自身を出力する Pythonプログラムを出力する Ruby プログラム – ruby prog.rb | python で自分自身が出たらOK – (Python 以外の別言語でもご自由に) – ヒント1:Python の Hello, world • つまり、出力を print " と " で囲めばよい – ヒント2:エスケープ回避が必要 • ¥ を書きたくなったら 92.chr と書く • 興味があれば 3言語版、4言語版、…も print "Hello, world!"

Slide 67

Slide 67 text

ネタ案 • もとに戻らず、変わり続ける Quine 風プログラム – ヒント:ベース形 • 実行するたびに n=1 の部分が n=2 、n=3 、…と変わっ ていくようにする • 3 回実行して元に戻る Quine 風プログラム – n=1 の部分が n=2 、n=3 、n=1 、n=2 、n=3 、 …と変わっていくようにする • 実行のたびに色が変わる Quine 風プログラム – N の値を元に色つけを変える n=1;eval(s=%q[puts %[n=1;eval(s=%q[#{ s }])]])

Slide 68

Slide 68 text

ネタ案 • 変な形状の Quine – こんなのとか  – アスキーアート化と うまく組み合わせると良い # ### ##### ####### ######### ########### ############# ############### ################# ###################

Slide 69

Slide 69 text

ランダムアイデア • 行単位で sort しても動くプログラム – ruby prog.rb でも – sort prog.rb | ruby – でも動作する • 有名でないオプションを悪用したプログラム – ruby –x とか ruby –i とか • 発想法 – 「○○を Quine にするとどうなるか?」 – 過去の TRICK や IOCCC の入賞エントリを 眺めてみるとアイデアが浮かぶかも • http://magazine.rubyist.net/?0043-TRICK2013 • http://magazine.rubyist.net/?0053-TRICK2015 • http://ioccc.org/years.html – (遠藤が超絶技巧をはじめたきっかけも IOCCC の コードリーディングです)