今年の年賀状ネタ
去年(http://d.hatena.ne.jp/rofi/20100417/1271519352)に引き続き今年もやりました。
長いので詳細は続きから。
今年のネタ
みなさん今年の干支は何でしょうか?
そうですね、みなさん大好きうさみみ年兎年ですね!
ということで今年のネタはこれです!
#include <stdio.h>/* q=" =;;; $a= 'us' .'a' .'c' .'c' ;; $a=~ s/c/mi/g ;print $a; exit(); q&*/ #/**/ define p(c) putc(c ,stdout ) void pr( char *s, int/* */ e) {char *t =s ;for (; *s ;++s){ /* */ switch( *s ){ case 96: if (e) p(*s);else pr (t ,1);break; /* */ case 94:;if (e) p( *s);else{int i; for (i=0;i<s[1]-s [2] ;++ i)p( 32);s+=2;;;} break ;case 37:if(e){int i;p (*s) ;p(s [1]);p (s[2]);p(34 );p( 10 );for(i=0;i<s [1 ]- s[ 2] ;0, ++ i)p(32);s+= 2; p( 34);;;;}else s+= 2; ;; break; case 95:if(e)p( *s); else s+=s[1]-s[2] ;; break ;; ;; case 10:; if (e) {p(92);p( 110 ); }/**/else p( 10 );break;; /* */ case 38: /* */ ;case 39: if (e) {p(92); p( 48 );p(52) ;if (* s ==38 )p (54) ;else 00, p( 55);} else p(*s);break; ;; case 34 :; case 92:if(e)p( 92);default:p(*s) ;}}}int main() {;/*" ; puts "Ha#{ #soqm "ppy" "" " Ne" "" "w"}#{ #kqlz " Ye" "ar!" }"; #lafksapkl ewfm ;;; exit();; 'dfgkmok qkozg so*/ if(00, sizeof (';' )==1) puts( "2011"); else{char*s="^yn#include^zb^zg <s%fa" "_xa tdio.h>/*\n^ie^ie q=\"^le=;;;^zb^zb^ke$a=_oa ^og%da" "_qa \047us\047\n^le.\047a\047^rg.\047c\047^zb^zb.\047c\047^vj;;_ia \n%ba" "^ii $a=~^ifs/c/mi/g^mk;print^zb^vj^ie^ie$a;^urexit();^ieq\046*/\n^ie#/**/^ibdefine p(%aa" "c) ^zb^vjputc(c ,stdout^ie )\n^ievoid^ie^ie pr(^ie char^zb^vj *s,^ie int/*^ie^ie */%rr" "\n^iee)^rg{char^le*t^zb^vj=s^og;for^vj(;\n^li*s^vj;++s){^le/*^zb^qg*/^leswitch(^rg*s\n^li){ ^vl%kk" "case 96:^keif^zb^og(e)^jep(*s);else^qgpr\n^li(t^rg,1);break;^ke/*^zb^ke*/^kecase 94:;if^qg(e)\n^mj%ee" "p(^qg*s);else{int^lfi;^zb^iefor^ie(i=0;i<s[1]-s^qg[2]\n^li;++^kei)p( 32);s+=2;;;}^libreak^zb;case %cc" " 37:if(e){int i;p^ke(*s)\n^li;p(s^ie[1]);p (s[2]);p(34^li);p(^za10^li);for(i=0;i<s [1 ]-^jes[\n%ww" "^ie 2] ;0,^ie++ i)p(32);s+=^li2;^zap(^ie34);;;;}else^lis+=^li2;^li;;\n^idbreak;^idcase 95:if(e)p(%tt" "^li*s);^vj^rgelse s+=s[1]-s[2] ;;^lebreak\n^ke;;^qg;; case 10:;^ieif^zb(e) {p(92);p(^li110\n^vj%oo" "^le); }/**/else^lip(^zb10^li);break;; /*\n^vj^og*/^licase 38: /*^zb*/ ;case 39: if\n^vj^pg(e) %99" " {p(92); p(^zb48 );p(52) ;if\n^xa(* s ==38 )p^xa(54) ;else 00,\n^za p( 55);} else^iep(*s);bre%;;" "ak;^ke;; case 34\n^za :; case 92:if(e)p(^pg92);default:p(*s)\n^zb^ie;}}}int^zb^lemain()\n^zc {;/*%pp" "\"^ie; puts^vj^le\"Ha#{^le#soqm\n^vj^pg\"ppy\"^ke\"\" \" Ne\"^xj\"\" \"w\"}#{^qh #kqlz\n^vf\" Ye%@@" "\"^rg\"ar!\" }\";^xj#lafksapkl^xjewfm\n^yj;;;^yjexit();;^yj\047dfgkmok^veqkozg\n^vjso*/^xfif(00,^ve%77" "sizeof^xd(\047;\047\n^ie^ie )==1)^zb^zb^xbputs(\n^ie^li\"2011\");^xbelse{char*s=\"`\";pr(s,0);}ret%##" "urn 0;}//\047;#\046;\n";pr(s,0);}return 0;}//';#&;
さてこれはなんでしょう?
そう!
うさぎですね!
#うさぎに見えない人もうさぎということにしておいてください><
ちなみに元画像はこれです。
もっとかわいいの描きたかったのですが絵が描けない自分にはこれが限界でした。
絵が描けるようになりたいです><
これからが本当の・・・
これで終わりだと思いましたか?
これだけで終わらないのが私です!
そもそも文字で絵を描くぐらいならそういうソフトウェアを使えばできますからね。
ではここで問題です。
これは一体なんでしょう?
polyglot
感のいい方ならこれがプログラムだと気づくと思います。
ではこれは何の言語のプログラムでしょう?
「#includeから察するにCかC++かな?」と思った方がいるのではと思います。
そしてそれは正しいです!
だがそれだけで終わらないのが私です!
実はこのAA、4つのプログラミング言語で実行することが可能です!
後にそれぞれの詳細は述べますが、このようにひとつのソースを複数の言語で実行できるものをpolyglotと言うそうです。
詳細はWikipediaを参照してください(英語)。
http://en.wikipedia.org/wiki/Polyglot_%28computing%29
1つめ
さて、まずは1つめの言語です。
先に述べたようにC言語/C++言語で実行できます。
C言語は少し後回しにしてC++言語で説明します。
あ、ちなみにC言語とC++言語は別物ですからね?
コード
#include <stdio.h>/* q=" =;;; $a= 'us' .'a' .'c' .'c' ;; $a=~ s/c/mi/g ;print $a; exit(); q&*/ #/**/ define p(c) putc(c ,stdout ) void pr( char *s, int/* */ e) {char *t =s ;for (; *s ;++s){ /* */ switch( *s ){ case 96: if (e) p(*s);else pr (t ,1);break; /* */ case 94:;if (e) p( *s);else{int i; for (i=0;i<s[1]-s [2] ;++ i)p( 32);s+=2;;;} break ;case 37:if(e){int i;p (*s) ;p(s [1]);p (s[2]);p(34 );p( 10 );for(i=0;i<s [1 ]- s[ 2] ;0, ++ i)p(32);s+= 2; p( 34);;;;}else s+= 2; ;; break; case 95:if(e)p( *s); else s+=s[1]-s[2] ;; break ;; ;; case 10:; if (e) {p(92);p( 110 ); }/**/else p( 10 );break;; /* */ case 38: /* */ ;case 39: if (e) {p(92); p( 48 );p(52) ;if (* s ==38 )p (54) ;else 00, p( 55);} else p(*s);break; ;; case 34 :; case 92:if(e)p( 92);default:p(*s) ;}}}int main() {;/*" ; puts "Ha#{ #soqm "ppy" "" " Ne" "" "w"}#{ #kqlz " Ye" "ar!" }"; #lafksapkl ewfm ;;; exit();; 'dfgkmok qkozg so*/ if(00, sizeof (';' )==1) puts( "2011"); else{char*s="^yn#include^zb^zg <s%fa" "_xa tdio.h>/*\n^ie^ie q=\"^le=;;;^zb^zb^ke$a=_oa ^og%da" "_qa \047us\047\n^le.\047a\047^rg.\047c\047^zb^zb.\047c\047^vj;;_ia \n%ba" "^ii $a=~^ifs/c/mi/g^mk;print^zb^vj^ie^ie$a;^urexit();^ieq\046*/\n^ie#/**/^ibdefine p(%aa" "c) ^zb^vjputc(c ,stdout^ie )\n^ievoid^ie^ie pr(^ie char^zb^vj *s,^ie int/*^ie^ie */%rr" "\n^iee)^rg{char^le*t^zb^vj=s^og;for^vj(;\n^li*s^vj;++s){^le/*^zb^qg*/^leswitch(^rg*s\n^li){ ^vl%kk" "case 96:^keif^zb^og(e)^jep(*s);else^qgpr\n^li(t^rg,1);break;^ke/*^zb^ke*/^kecase 94:;if^qg(e)\n^mj%ee" "p(^qg*s);else{int^lfi;^zb^iefor^ie(i=0;i<s[1]-s^qg[2]\n^li;++^kei)p( 32);s+=2;;;}^libreak^zb;case %cc" " 37:if(e){int i;p^ke(*s)\n^li;p(s^ie[1]);p (s[2]);p(34^li);p(^za10^li);for(i=0;i<s [1 ]-^jes[\n%ww" "^ie 2] ;0,^ie++ i)p(32);s+=^li2;^zap(^ie34);;;;}else^lis+=^li2;^li;;\n^idbreak;^idcase 95:if(e)p(%tt" "^li*s);^vj^rgelse s+=s[1]-s[2] ;;^lebreak\n^ke;;^qg;; case 10:;^ieif^zb(e) {p(92);p(^li110\n^vj%oo" "^le); }/**/else^lip(^zb10^li);break;; /*\n^vj^og*/^licase 38: /*^zb*/ ;case 39: if\n^vj^pg(e) %99" " {p(92); p(^zb48 );p(52) ;if\n^xa(* s ==38 )p^xa(54) ;else 00,\n^za p( 55);} else^iep(*s);bre%;;" "ak;^ke;; case 34\n^za :; case 92:if(e)p(^pg92);default:p(*s)\n^zb^ie;}}}int^zb^lemain()\n^zc {;/*%pp" "\"^ie; puts^vj^le\"Ha#{^le#soqm\n^vj^pg\"ppy\"^ke\"\" \" Ne\"^xj\"\" \"w\"}#{^qh #kqlz\n^vf\" Ye%@@" "\"^rg\"ar!\" }\";^xj#lafksapkl^xjewfm\n^yj;;;^yjexit();;^yj\047dfgkmok^veqkozg\n^vjso*/^xfif(00,^ve%77" "sizeof^xd(\047;\047\n^ie^ie )==1)^zb^zb^xbputs(\n^ie^li\"2011\");^xbelse{char*s=\"`\";pr(s,0);}ret%##" "urn 0;}//\047;#\046;\n";pr(s,0);}return 0;}//';#&;
実行結果
%g++ nyc.cpp nyc.cpp: In function ‘int main()’: nyc.cpp:47: 警告: deprecated conversion from string constant to ‘char*’ %./a.exe 2011 %
2つめ
さて2つめです。
何で動くと思いますか?
実はperlで動きます。
では実行してみましょう。
コード
#include <stdio.h>/* q=" =;;; $a= 'us' .'a' .'c' .'c' ;; $a=~ s/c/mi/g ;print $a; exit(); q&*/ #/**/ define p(c) putc(c ,stdout ) void pr( char *s, int/* */ e) {char *t =s ;for (; *s ;++s){ /* */ switch( *s ){ case 96: if (e) p(*s);else pr (t ,1);break; /* */ case 94:;if (e) p( *s);else{int i; for (i=0;i<s[1]-s [2] ;++ i)p( 32);s+=2;;;} break ;case 37:if(e){int i;p (*s) ;p(s [1]);p (s[2]);p(34 );p( 10 );for(i=0;i<s [1 ]- s[ 2] ;0, ++ i)p(32);s+= 2; p( 34);;;;}else s+= 2; ;; break; case 95:if(e)p( *s); else s+=s[1]-s[2] ;; break ;; ;; case 10:; if (e) {p(92);p( 110 ); }/**/else p( 10 );break;; /* */ case 38: /* */ ;case 39: if (e) {p(92); p( 48 );p(52) ;if (* s ==38 )p (54) ;else 00, p( 55);} else p(*s);break; ;; case 34 :; case 92:if(e)p( 92);default:p(*s) ;}}}int main() {;/*" ; puts "Ha#{ #soqm "ppy" "" " Ne" "" "w"}#{ #kqlz " Ye" "ar!" }"; #lafksapkl ewfm ;;; exit();; 'dfgkmok qkozg so*/ if(00, sizeof (';' )==1) puts( "2011"); else{char*s="^yn#include^zb^zg <s%fa" "_xa tdio.h>/*\n^ie^ie q=\"^le=;;;^zb^zb^ke$a=_oa ^og%da" "_qa \047us\047\n^le.\047a\047^rg.\047c\047^zb^zb.\047c\047^vj;;_ia \n%ba" "^ii $a=~^ifs/c/mi/g^mk;print^zb^vj^ie^ie$a;^urexit();^ieq\046*/\n^ie#/**/^ibdefine p(%aa" "c) ^zb^vjputc(c ,stdout^ie )\n^ievoid^ie^ie pr(^ie char^zb^vj *s,^ie int/*^ie^ie */%rr" "\n^iee)^rg{char^le*t^zb^vj=s^og;for^vj(;\n^li*s^vj;++s){^le/*^zb^qg*/^leswitch(^rg*s\n^li){ ^vl%kk" "case 96:^keif^zb^og(e)^jep(*s);else^qgpr\n^li(t^rg,1);break;^ke/*^zb^ke*/^kecase 94:;if^qg(e)\n^mj%ee" "p(^qg*s);else{int^lfi;^zb^iefor^ie(i=0;i<s[1]-s^qg[2]\n^li;++^kei)p( 32);s+=2;;;}^libreak^zb;case %cc" " 37:if(e){int i;p^ke(*s)\n^li;p(s^ie[1]);p (s[2]);p(34^li);p(^za10^li);for(i=0;i<s [1 ]-^jes[\n%ww" "^ie 2] ;0,^ie++ i)p(32);s+=^li2;^zap(^ie34);;;;}else^lis+=^li2;^li;;\n^idbreak;^idcase 95:if(e)p(%tt" "^li*s);^vj^rgelse s+=s[1]-s[2] ;;^lebreak\n^ke;;^qg;; case 10:;^ieif^zb(e) {p(92);p(^li110\n^vj%oo" "^le); }/**/else^lip(^zb10^li);break;; /*\n^vj^og*/^licase 38: /*^zb*/ ;case 39: if\n^vj^pg(e) %99" " {p(92); p(^zb48 );p(52) ;if\n^xa(* s ==38 )p^xa(54) ;else 00,\n^za p( 55);} else^iep(*s);bre%;;" "ak;^ke;; case 34\n^za :; case 92:if(e)p(^pg92);default:p(*s)\n^zb^ie;}}}int^zb^lemain()\n^zc {;/*%pp" "\"^ie; puts^vj^le\"Ha#{^le#soqm\n^vj^pg\"ppy\"^ke\"\" \" Ne\"^xj\"\" \"w\"}#{^qh #kqlz\n^vf\" Ye%@@" "\"^rg\"ar!\" }\";^xj#lafksapkl^xjewfm\n^yj;;;^yjexit();;^yj\047dfgkmok^veqkozg\n^vjso*/^xfif(00,^ve%77" "sizeof^xd(\047;\047\n^ie^ie )==1)^zb^zb^xbputs(\n^ie^li\"2011\");^xbelse{char*s=\"`\";pr(s,0);}ret%##" "urn 0;}//\047;#\046;\n";pr(s,0);}return 0;}//';#&;
実行結果
%perl nyc.pl usamimi %
みんな大好きうさみみと表示されました。
今年はなんと兎年らしいですよ!
このようにちゃんと実行できてさらにメッセージも異なりますね!
解説
さて、このコードの説明です。
はてダの色分けがおかしいようなので読みやすいように少しいじったものを載せます。
#include <stdio.h>/* q=" =;;; $a= 'us' .'a' .'c' .'c' ;; $a=~ s/c/mi/g ;print $a; exit(); q&*/ #/**/ define p(c) putc(c ,stdout ) void pr( char *s, int/* */ e) {char *t =s ;for (; *s ;++s){ /* */ switch( *s ){ case 96: if (e) p(*s);else pr (t ,1);break; /* */ case 94:;if (e) p( *s);else{int i; for (i=0;i<s[1]-s [2] ;++ i)p( 32);s+=2;;;} break ;case 37:if(e){int i;p (*s) ;p(s [1]);p (s[2]);p(34 );p( 10 );for(i=0;i<s [1 ]- s[ 2] ;0, ++ i)p(32);s+= 2; p( 34);;;;}else s+= 2; ;; break; case 95:if(e)p( *s); else s+=s[1]-s[2] ;; break ;; ;; case 10:; if (e) {p(92);p( 110 ); }/**/else p( 10 );break;; /* */ case 38: /* */ ;case 39: if (e) {p(92); p( 48 );p(52) ;if (* s ==38 )p (54) ;else 00, p( 55);} else p(*s);break; ;; case 34 :; case 92:if(e)p( 92);default:p(*s) ;}}}int main() {;/*" ; puts "Ha#{ #soqm "ppy" "" " Ne" "" "w"}#{ #kqlz " Ye" "ar!" }"; #lafksapkl ewfm ;;; exit();; 'dfgkmok qkozg so*/ if(00, sizeof (';' )==1) puts( "2011"); else{char*s="^yn#include^zb^zg <s%fa" "_xa tdio.h>/*\n^ie^ie q=\"^le=;;;^zb^zb^ke$a=_oa ^og%da" "_qa \047us\047\n^le.\047a\047^rg.\047c\047^zb^zb.\047c\047^vj;;_ia \n%ba" "^ii $a=~^ifs/c/mi/g^mk;print^zb^vj^ie^ie$a;^urexit();^ieq\046*/\n^ie#/**/^ibdefine p(%aa" "c) ^zb^vjputc(c ,stdout^ie )\n^ievoid^ie^ie pr(^ie char^zb^vj *s,^ie int/*^ie^ie */%rr" "\n^iee)^rg{char^le*t^zb^vj=s^og;for^vj(;\n^li*s^vj;++s){^le/*^zb^qg*/^leswitch(^rg*s\n^li){ ^vl%kk" "case 96:^keif^zb^og(e)^jep(*s);else^qgpr\n^li(t^rg,1);break;^ke/*^zb^ke*/^kecase 94:;if^qg(e)\n^mj%ee" "p(^qg*s);else{int^lfi;^zb^iefor^ie(i=0;i<s[1]-s^qg[2]\n^li;++^kei)p( 32);s+=2;;;}^libreak^zb;case %cc" " 37:if(e){int i;p^ke(*s)\n^li;p(s^ie[1]);p (s[2]);p(34^li);p(^za10^li);for(i=0;i<s [1 ]-^jes[\n%ww" "^ie 2] ;0,^ie++ i)p(32);s+=^li2;^zap(^ie34);;;;}else^lis+=^li2;^li;;\n^idbreak;^idcase 95:if(e)p(%tt" "^li*s);^vj^rgelse s+=s[1]-s[2] ;;^lebreak\n^ke;;^qg;; case 10:;^ieif^zb(e) {p(92);p(^li110\n^vj%oo" "^le); }/**/else^lip(^zb10^li);break;; /*\n^vj^og*/^licase 38: /*^zb*/ ;case 39: if\n^vj^pg(e) %99" " {p(92); p(^zb48 );p(52) ;if\n^xa(* s ==38 )p^xa(54) ;else 00,\n^za p( 55);} else^iep(*s);bre%;;" "ak;^ke;; case 34\n^za :; case 92:if(e)p(^pg92);default:p(*s)\n^zb^ie;}}}int^zb^lemain()\n^zc {;/*%pp" "\"^ie; puts^vj^le\"Ha#{^le#soqm\n^vj^pg\"ppy\"^ke\"\" \" Ne\"^xj\"\" \"w\"}#{^qh #kqlz\n^vf\" Ye%@@" "\"^rg\"ar!\" }\";^xj#lafksapkl^xjewfm\n^yj;;;^yjexit();;^yj\047dfgkmok^veqkozg\n^vjso*/^xfif(00,^ve%77" "sizeof^xd(\047;\047\n^ie^ie )==1)^zb^zb^xbputs(\n^ie^li\"2011\");^xbelse{char*s=\"`\";pr(s,0);}ret%##" "urn 0;}//\047;#\046;\n";pr(s,0);}return 0;}//';#&;
緑がコメント、青文字が文字列です。
見ての通り大半が文字列です。
ということでこれを読みやすいように主要のコードのみ載せて整形すると以下のようになります。
#include 以下略。#の行はコメントなのでinclude行はコメント q=" =;;; q==で文字列のクオート。qの後に続く記号で囲んだ部分が文字列扱いになる $a='us'.'a'.'c'.'c';; perlでは文字列の結合には.を使う $a=~s/c/mi/g; 文字列の置換。'c'を'mi'に置換する print $a; 変数$aを出力 exit(); プログラムの終了 q&*/ 途中略。この部分はすべて文字列扱い "urn 0;}//\047;#\046;\n";pr(s,0);}return 0;}//';#&;
赤文字で書いたものが解説です。
ようは'usacc'という文字列を作り出した後、'c'を'mi'に置換することによって'usamimi'を作り出した後それを出力しているだけに過ぎません。
とっても簡単ですね!
ちなみにqの後に続く記号で囲んだものは''で囲ったように文字列となります。
よくs/hoge/fuga/というふうに置換を行ったりしますが別に/である必要はありません。
この/の代わりに別の記号が使えます。
よってここでは=を用いていたり&を用いていたりします。
3つめ
さて3つめです。
なにで動くと思いますか?
実はrubyで動きます。
ではこちらも実行してみましょう。
コード
#include <stdio.h>/* q=" =;;; $a= 'us' .'a' .'c' .'c' ;; $a=~ s/c/mi/g ;print $a; exit(); q&*/ #/**/ define p(c) putc(c ,stdout ) void pr( char *s, int/* */ e) {char *t =s ;for (; *s ;++s){ /* */ switch( *s ){ case 96: if (e) p(*s);else pr (t ,1);break; /* */ case 94:;if (e) p( *s);else{int i; for (i=0;i<s[1]-s [2] ;++ i)p( 32);s+=2;;;} break ;case 37:if(e){int i;p (*s) ;p(s [1]);p (s[2]);p(34 );p( 10 );for(i=0;i<s [1 ]- s[ 2] ;0, ++ i)p(32);s+= 2; p( 34);;;;}else s+= 2; ;; break; case 95:if(e)p( *s); else s+=s[1]-s[2] ;; break ;; ;; case 10:; if (e) {p(92);p( 110 ); }/**/else p( 10 );break;; /* */ case 38: /* */ ;case 39: if (e) {p(92); p( 48 );p(52) ;if (* s ==38 )p (54) ;else 00, p( 55);} else p(*s);break; ;; case 34 :; case 92:if(e)p( 92);default:p(*s) ;}}}int main() {;/*" ; puts "Ha#{ #soqm "ppy" "" " Ne" "" "w"}#{ #kqlz " Ye" "ar!" }"; #lafksapkl ewfm ;;; exit();; 'dfgkmok qkozg so*/ if(00, sizeof (';' )==1) puts( "2011"); else{char*s="^yn#include^zb^zg <s%fa" "_xa tdio.h>/*\n^ie^ie q=\"^le=;;;^zb^zb^ke$a=_oa ^og%da" "_qa \047us\047\n^le.\047a\047^rg.\047c\047^zb^zb.\047c\047^vj;;_ia \n%ba" "^ii $a=~^ifs/c/mi/g^mk;print^zb^vj^ie^ie$a;^urexit();^ieq\046*/\n^ie#/**/^ibdefine p(%aa" "c) ^zb^vjputc(c ,stdout^ie )\n^ievoid^ie^ie pr(^ie char^zb^vj *s,^ie int/*^ie^ie */%rr" "\n^iee)^rg{char^le*t^zb^vj=s^og;for^vj(;\n^li*s^vj;++s){^le/*^zb^qg*/^leswitch(^rg*s\n^li){ ^vl%kk" "case 96:^keif^zb^og(e)^jep(*s);else^qgpr\n^li(t^rg,1);break;^ke/*^zb^ke*/^kecase 94:;if^qg(e)\n^mj%ee" "p(^qg*s);else{int^lfi;^zb^iefor^ie(i=0;i<s[1]-s^qg[2]\n^li;++^kei)p( 32);s+=2;;;}^libreak^zb;case %cc" " 37:if(e){int i;p^ke(*s)\n^li;p(s^ie[1]);p (s[2]);p(34^li);p(^za10^li);for(i=0;i<s [1 ]-^jes[\n%ww" "^ie 2] ;0,^ie++ i)p(32);s+=^li2;^zap(^ie34);;;;}else^lis+=^li2;^li;;\n^idbreak;^idcase 95:if(e)p(%tt" "^li*s);^vj^rgelse s+=s[1]-s[2] ;;^lebreak\n^ke;;^qg;; case 10:;^ieif^zb(e) {p(92);p(^li110\n^vj%oo" "^le); }/**/else^lip(^zb10^li);break;; /*\n^vj^og*/^licase 38: /*^zb*/ ;case 39: if\n^vj^pg(e) %99" " {p(92); p(^zb48 );p(52) ;if\n^xa(* s ==38 )p^xa(54) ;else 00,\n^za p( 55);} else^iep(*s);bre%;;" "ak;^ke;; case 34\n^za :; case 92:if(e)p(^pg92);default:p(*s)\n^zb^ie;}}}int^zb^lemain()\n^zc {;/*%pp" "\"^ie; puts^vj^le\"Ha#{^le#soqm\n^vj^pg\"ppy\"^ke\"\" \" Ne\"^xj\"\" \"w\"}#{^qh #kqlz\n^vf\" Ye%@@" "\"^rg\"ar!\" }\";^xj#lafksapkl^xjewfm\n^yj;;;^yjexit();;^yj\047dfgkmok^veqkozg\n^vjso*/^xfif(00,^ve%77" "sizeof^xd(\047;\047\n^ie^ie )==1)^zb^zb^xbputs(\n^ie^li\"2011\");^xbelse{char*s=\"`\";pr(s,0);}ret%##" "urn 0;}//\047;#\046;\n";pr(s,0);}return 0;}//';#&;
実行結果
%ruby nyc.rb Happy New Year! %
"Happy New Year!"と表示されました。
みなさん明けましておめでとうございます!(遅っ
このようにrubyでもちゃんと動き、さらにメッセージも異なります。
解説
ではコードの説明です。
rubyでは正しく色分けされてるようなので上記のコードを見てください。
赤色の部分はすべて文字列です。
また読みやすいように整形すると以下のようになります。
#include 以下略。#行はperl同様コメント q=" 中略。ここはすべて文字列 {;/*"; puts "Ha#{ #soqm 文字列は""で囲う。""で囲われた中では#{}は解釈される。この中はrubyで使える式が書け、その式が評価された結果がその部分の文字列になる。 "ppy" "" " Ne" "" "w"}#{ #kqlz また#{}中でもコメントは使用可。なので緑部分はコメント " Ye" "ar!" }";;;; rubyでの文字列定数の結合は普通に"hoge" "fuga"で"hogefuga"になる。 exit();; 'dfgkmok 中略。ここも文字列 sizeof('; ')==1) 中略。ここも文字列 rn 0;}//';#&;
perlではq= 〜 =が文字列扱いとなりましたが、rubyではq="〜"でqという変数に""の部分の文字列が代入されるという意味になります。
この違いによって同じコードでも文字列の終端の位置を言語によってずらし、片方の言語では実行されるがもう片方の言語では文字列扱いとなるようにして複数の言語のコードを埋め込みます。
処理内容としてはこれも単純で単純に"Happy New Year!"という文字列をprintで出力してexitしているだけです。
文字列の生成の仕方が少し奇妙ですが、やってることはこれも単純で、rubyでは""で囲われた文字列中で#{}があるとこの部分が評価されて文字列に置換されます。
#{}の中には式を書くことができ、この式が評価された結果にその部分が置換されます。
#{}の中で何をやってるかというと単に文字列の結合だけです。
これもとっても簡単ですね!
4つめ
さて最後です。
なぜC言語を最後に持ってきたかわかりますか?
そう、一番手が込んでるからです!
では実行してみましょう。
コード
#include <stdio.h>/* q=" =;;; $a= 'us' .'a' .'c' .'c' ;; $a=~ s/c/mi/g ;print $a; exit(); q&*/ #/**/ define p(c) putc(c ,stdout ) void pr( char *s, int/* */ e) {char *t =s ;for (; *s ;++s){ /* */ switch( *s ){ case 96: if (e) p(*s);else pr (t ,1);break; /* */ case 94:;if (e) p( *s);else{int i; for (i=0;i<s[1]-s [2] ;++ i)p( 32);s+=2;;;} break ;case 37:if(e){int i;p (*s) ;p(s [1]);p (s[2]);p(34 );p( 10 );for(i=0;i<s [1 ]- s[ 2] ;0, ++ i)p(32);s+= 2; p( 34);;;;}else s+= 2; ;; break; case 95:if(e)p( *s); else s+=s[1]-s[2] ;; break ;; ;; case 10:; if (e) {p(92);p( 110 ); }/**/else p( 10 );break;; /* */ case 38: /* */ ;case 39: if (e) {p(92); p( 48 );p(52) ;if (* s ==38 )p (54) ;else 00, p( 55);} else p(*s);break; ;; case 34 :; case 92:if(e)p( 92);default:p(*s) ;}}}int main() {;/*" ; puts "Ha#{ #soqm "ppy" "" " Ne" "" "w"}#{ #kqlz " Ye" "ar!" }"; #lafksapkl ewfm ;;; exit();; 'dfgkmok qkozg so*/ if(00, sizeof (';' )==1) puts( "2011"); else{char*s="^yn#include^zb^zg <s%fa" "_xa tdio.h>/*\n^ie^ie q=\"^le=;;;^zb^zb^ke$a=_oa ^og%da" "_qa \047us\047\n^le.\047a\047^rg.\047c\047^zb^zb.\047c\047^vj;;_ia \n%ba" "^ii $a=~^ifs/c/mi/g^mk;print^zb^vj^ie^ie$a;^urexit();^ieq\046*/\n^ie#/**/^ibdefine p(%aa" "c) ^zb^vjputc(c ,stdout^ie )\n^ievoid^ie^ie pr(^ie char^zb^vj *s,^ie int/*^ie^ie */%rr" "\n^iee)^rg{char^le*t^zb^vj=s^og;for^vj(;\n^li*s^vj;++s){^le/*^zb^qg*/^leswitch(^rg*s\n^li){ ^vl%kk" "case 96:^keif^zb^og(e)^jep(*s);else^qgpr\n^li(t^rg,1);break;^ke/*^zb^ke*/^kecase 94:;if^qg(e)\n^mj%ee" "p(^qg*s);else{int^lfi;^zb^iefor^ie(i=0;i<s[1]-s^qg[2]\n^li;++^kei)p( 32);s+=2;;;}^libreak^zb;case %cc" " 37:if(e){int i;p^ke(*s)\n^li;p(s^ie[1]);p (s[2]);p(34^li);p(^za10^li);for(i=0;i<s [1 ]-^jes[\n%ww" "^ie 2] ;0,^ie++ i)p(32);s+=^li2;^zap(^ie34);;;;}else^lis+=^li2;^li;;\n^idbreak;^idcase 95:if(e)p(%tt" "^li*s);^vj^rgelse s+=s[1]-s[2] ;;^lebreak\n^ke;;^qg;; case 10:;^ieif^zb(e) {p(92);p(^li110\n^vj%oo" "^le); }/**/else^lip(^zb10^li);break;; /*\n^vj^og*/^licase 38: /*^zb*/ ;case 39: if\n^vj^pg(e) %99" " {p(92); p(^zb48 );p(52) ;if\n^xa(* s ==38 )p^xa(54) ;else 00,\n^za p( 55);} else^iep(*s);bre%;;" "ak;^ke;; case 34\n^za :; case 92:if(e)p(^pg92);default:p(*s)\n^zb^ie;}}}int^zb^lemain()\n^zc {;/*%pp" "\"^ie; puts^vj^le\"Ha#{^le#soqm\n^vj^pg\"ppy\"^ke\"\" \" Ne\"^xj\"\" \"w\"}#{^qh #kqlz\n^vf\" Ye%@@" "\"^rg\"ar!\" }\";^xj#lafksapkl^xjewfm\n^yj;;;^yjexit();;^yj\047dfgkmok^veqkozg\n^vjso*/^xfif(00,^ve%77" "sizeof^xd(\047;\047\n^ie^ie )==1)^zb^zb^xbputs(\n^ie^li\"2011\");^xbelse{char*s=\"`\";pr(s,0);}ret%##" "urn 0;}//\047;#\046;\n";pr(s,0);}return 0;}//';#&;
実行結果
%gcc nyc.c %./a.exe #include <stdio.h>/* q=" =;;; $a= 'us' .'a' .'c' .'c' ;; $a=~ s/c/mi/g ;print $a; exit(); q&*/ #/**/ define p(c) putc(c ,stdout ) void pr( char *s, int/* */ e) {char *t =s ;for (; *s ;++s){ /* */ switch( *s ){ case 96: if (e) p(*s);else pr (t ,1);break; /* */ case 94:;if (e) p( *s);else{int i; for (i=0;i<s[1]-s [2] ;++ i)p( 32);s+=2;;;} break ;case 37:if(e){int i;p (*s) ;p(s [1]);p (s[2]);p(34 );p( 10 );for(i=0;i<s [1 ]- s[ 2] ;0, ++ i)p(32);s+= 2; p( 34);;;;}else s+= 2; ;; break; case 95:if(e)p( *s); else s+=s[1]-s[2] ;; break ;; ;; case 10:; if (e) {p(92);p( 110 ); }/**/else p( 10 );break;; /* */ case 38: /* */ ;case 39: if (e) {p(92); p( 48 );p(52) ;if (* s ==38 )p (54) ;else 00, p( 55);} else p(*s);break; ;; case 34 :; case 92:if(e)p( 92);default:p(*s) ;}}}int main() {;/*" ; puts "Ha#{ #soqm "ppy" "" " Ne" "" "w"}#{ #kqlz " Ye" "ar!" }"; #lafksapkl ewfm ;;; exit();; 'dfgkmok qkozg so*/ if(00, sizeof (';' )==1) puts( "2011"); else{char*s="^yn#include^zb^zg <s%fa" "_xa tdio.h>/*\n^ie^ie q=\"^le=;;;^zb^zb^ke$a=_oa ^og%da" "_qa \047us\047\n^le.\047a\047^rg.\047c\047^zb^zb.\047c\047^vj;;_ia \n%ba" "^ii $a=~^ifs/c/mi/g^mk;print^zb^vj^ie^ie$a;^urexit();^ieq\046*/\n^ie#/**/^ibdefine p(%aa" "c) ^zb^vjputc(c ,stdout^ie )\n^ievoid^ie^ie pr(^ie char^zb^vj *s,^ie int/*^ie^ie */%rr" "\n^iee)^rg{char^le*t^zb^vj=s^og;for^vj(;\n^li*s^vj;++s){^le/*^zb^qg*/^leswitch(^rg*s\n^li){ ^vl%kk" "case 96:^keif^zb^og(e)^jep(*s);else^qgpr\n^li(t^rg,1);break;^ke/*^zb^ke*/^kecase 94:;if^qg(e)\n^mj%ee" "p(^qg*s);else{int^lfi;^zb^iefor^ie(i=0;i<s[1]-s^qg[2]\n^li;++^kei)p( 32);s+=2;;;}^libreak^zb;case %cc" " 37:if(e){int i;p^ke(*s)\n^li;p(s^ie[1]);p (s[2]);p(34^li);p(^za10^li);for(i=0;i<s [1 ]-^jes[\n%ww" "^ie 2] ;0,^ie++ i)p(32);s+=^li2;^zap(^ie34);;;;}else^lis+=^li2;^li;;\n^idbreak;^idcase 95:if(e)p(%tt" "^li*s);^vj^rgelse s+=s[1]-s[2] ;;^lebreak\n^ke;;^qg;; case 10:;^ieif^zb(e) {p(92);p(^li110\n^vj%oo" "^le); }/**/else^lip(^zb10^li);break;; /*\n^vj^og*/^licase 38: /*^zb*/ ;case 39: if\n^vj^pg(e) %99" " {p(92); p(^zb48 );p(52) ;if\n^xa(* s ==38 )p^xa(54) ;else 00,\n^za p( 55);} else^iep(*s);bre%;;" "ak;^ke;; case 34\n^za :; case 92:if(e)p(^pg92);default:p(*s)\n^zb^ie;}}}int^zb^lemain()\n^zc {;/*%pp" "\"^ie; puts^vj^le\"Ha#{^le#soqm\n^vj^pg\"ppy\"^ke\"\" \" Ne\"^xj\"\" \"w\"}#{^qh #kqlz\n^vf\" Ye%@@" "\"^rg\"ar!\" }\";^xj#lafksapkl^xjewfm\n^yj;;;^yjexit();;^yj\047dfgkmok^veqkozg\n^vjso*/^xfif(00,^ve%77" "sizeof^xd(\047;\047\n^ie^ie )==1)^zb^zb^xbputs(\n^ie^li\"2011\");^xbelse{char*s=\"`\";pr(s,0);}ret%##" "urn 0;}//\047;#\046;\n";pr(s,0);}return 0;}//';#&; %
おわかりいただけただろうか?
そう、自分自身のコードを出力しているのです。
しかも一字一句、1byteもずれずに!
このように自分自身のコードを出力するプログラムをクワイン(Quine)と言います。
クワインは自分の出力に自分自身を出力するため、この出力をもう一度コンパイルすることもできますし実行することもできます。
その出力は当然また自分自身のコードとなります。
詳細な説明はこれもWikipediaあたりでも参照してください。
http://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AF%E3%82%A4%E3%83%B3_%28%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%29
一度クワインを組んだ方ならわかると思いますが自分自身を出力するのは意外と難しいです。
プログラム全体を出力しようとすると今度はその出力する文字列定数が出力できなかったり、出力を二度しようとするとどうやって二度出力するのかが問題になったり、さらにはエスケープ文字の問題があったりと結構難しいです。
特に今回は絵を表す以上空白が大量に入り、でも文字列リテラル中には自由に空白は使えないのでこのあたりの対処もする必要があるなどとても苦労しました。
さてくだらない話はここまでにして解説です。
このプログラムの主要なコードを載せます。
このコードを元に追加を行っていってできたのが上記です。
#include <stdio.h> #define p(c) putc(c,stdout) void pr( char *s, int e ) { char *t = s; for( ; *s ; ++s ) { switch( *s ) { case 96: /* '`' */ if( e ) p( *s ); else pr( t, 1 ); break; case 94: /* '^' */ if( e ) p( *s ); else { int i; for( i = 0 ; i < s[1] - s[2] ; ++i ) p( 32 ); /* ' ' */ s += 2; } break; case 37: /* '%' */ if( e ) { int i; p( *s ); p( s[1] ); p( s[2] ); p( 34 ); /* '"' */ p( 10 ); /* '\n' */ for( i = 0 ; i < s[1] - s[2] ; ++i ) p( 32 ); /* ' ' */ s += 2; p( 34 ); /* '"' */ } else s += 2; break; case 95: /* '_' */ if( e ) p( *s ); else s += s[1] - s[2]; break; case 10: /* '\n' */ if( e ) { p( 92 ); /* '\\' */ p( 110 ); /* 'n' */ } else p( 10 ); /* '\n' */ break; case 38: /* '&' */ case 39: /* '\'' */ if( e ) { p( 92 ); /* '\\' */ p( 48 ); /* '0' */ p( 52 ); /* '4' */ if( *s == 38 ) p( 54 ); /* '6' */ else p( 55 ); /* '7' */ } else p( *s ); break; case 34: /* '"' */ case 92: /* '\\' */ if( e ) p( 92 ); /* '\\' */ default: p( *s ); } } } int main() { char *s = "`"; pr( s, 0 ); }
main関数が違うのとコメントの部分は省いてありますが、このコードを年賀状のコードに使用しているので、年賀状のコードを整形すると上記のようなものになるはずです。
pr関数ですがこれは出力を行う関数です。
ただし、文字列リテラルの部分ではもう一度同じ文字列を出力するとともにエスケープするところはエスケープするなどの処理を行わないといけないため、第一引数に出力を行う文字列を、第二引数にそのまま出力を行うか処理をするのかのフラグを指定します。
また、単なるクワインであればエスケープ程度で済むのですが今回はAAを出すために文字列の位置がとても重要になります。
AAを出すために都合の良い位置に文字列が来てくれるはずもないのでそれらを調整するための特別なコマンドも使用しています。
それらのためにcaseやp関数(マクロによってputc関数に展開される)の引数には数字が直接使われています。
これらは自分自身のコードを出力するときに制御用なのか単にcase部分なのかの判断が簡単につかないため、わざとこうすることによってその問題を解決しています。
pr関数が出力の整形やエスケープ処理などを行ってくれるので残りはmain関数です。
main関数は成形すると以下のコードになっています。
int main() { ;/* コメント。ここの部分にrubyのコードが含まれている */ if( 00, sizeof( ';' ) == 1 ) 0から始まる数字は8進数表記。順次演算子で結局00の部分は意味を成さない。またsizeofに;を使ってるのはrubyのときに'で文字列の終端になってしまうため、;で文を区切り、さらに'で文字列を開始させることによってエラーとさせないようにしている。 puts( "2011" ); C++言語だとこの部分が実行される。 else { C言語だとこの部分が実行される。 char *s = "^yn#include^zb^zg中略。ここは文字列でこのコード自身が含まれる。 "urn 0;}//\047;#\046;\n"; pr(s,0); } return 0; }
ifで';'の大きさを調べてそれによって処理を振り分けます。
C++だと文字定数は1になるのでputs関数が実行されます。
これによってC++では"2011"と表示されたのでした。
一方C言語ではそうはならないためelse文が実行されpr関数によって自分自身のコードが出力されます。
またsizeofの部分で文字定数に;を使っているのはrubyで実行したときにC/C++で文字定数の開始の'がrubyでは文字列の終端になってしまうため、;で文を終了させ、さらにC/C++での文字定数の終端の'をrubyでの文字列の開始にさせることによってそれぞれでエラーなく通すことをさせるためにこれが使われています。
クワインを出力するための文字列は通常のクワインと比べて少しコマンドチックになってます。
これはさっきも述べたように無闇矢鱈に文字列を使うわけにはいかず、この文字列も整形できるようにするためにいくつかのコマンドが埋まっています。
例えばコードとして出力するときには文字を出力するのではなく代わりに空白文字をn個分出力せよみたいな形です。
これらのコマンドは詳しくは述べませんので興味があればpr関数を読んで解析してみてくださいw
まとめ
結局これはなんだったのかというと以下のようになります。
これらがひとつのテキストデータに混ざっていたのです。
とってもすごいですね!
#いや〜、よく頑張った >自分
あとがき
今回も無茶してみました。
構想は去年が始まってそこそこの頃からあったのですが、まともに手をつけ始めたのが大晦日の終わりごろ。
出来上がったのが正月という怠惰さ!
あまりにもひどいですね・・・
ちなみにwhitespaceやpythonも混ぜようかという構想も少しありました。
しかし、AAにする以上空白はAAのために使われるためwhitespaceは断念されました。
さらにpythonも空白がブロックの意味を成すため、空白が自由に使える必要がある今回では混ぜることができませんでした。
インデントが意味を持つ言語はこういうのには向かないですね (当たり前
ちなみにこんなプログラム書いておいてなんですが私はperl/rubyは使えません。
そのため必要最小限調べて書きましたw
実はこのネタを引っさげて高専カンファで話そうかなぁ〜とも思ったのですが結局しゃべる勇気が出ずに話しませんでした。
こんなことでは何時まで経ってもできないんですけどね〜・・・
さて、最後にいかがだったでしょうか?
今回は前回とは違い高級言語でやってみました。
今回のは相当手を込んで作ったので楽しんでいただけたなら幸いです。
ちなみに来年もやるかどうかはわかりません。
現状ではネタが全く決まってませんし・・・
この記事の☆の数によって来年もやるかどうか決めようかなぁ〜(ぁ
その前にそもそも出す相手g(ry