はじめてのQuineリレー
概要
昨日、自分自身を出力するHaskellプログラムを生成するOCamlプログラムを生成するSchemeプログラムを生成するRubyプログラムを生成するJavaScriptプログラムを生成するCプログラムを書いた。
#include<stdio.h> int main(){char*g="var p=print;p('puts %%^(format #t!Printf.printf~s~s!(x->string`main=putStr%%S)^.tr *?!...?#;p <<_;$><<?)');p('#include<stdio.h>');print(%cint main(){char*g=%c%s%c;printf(g,47,34,g,34,47);}%c.source);p('_')";printf(g,47,34,g,34,47);}
以下のように動作する。Makefileは後述。
$ make diff clang -std=c99 -Wall -W -Werror -pedantic -o quine-relay-c start.c && ./quine-relay-c > quine-relay.js v8 --use_strict quine-relay.js > quine-relay.rb ruby quine-relay.rb > quine-relay.scm gosh quine-relay.scm > quine-relay.ml ocaml quine-relay.ml > quine-relay.hs runghc quine-relay.hs > goal.c diff -u start.c goal.c $ wc start.c quine-relay.* 2 9 270 start.c 0 8 287 quine-relay.hs 0 13 402 quine-relay.js 0 8 304 quine-relay.ml 4 15 360 quine-relay.rb 2 11 333 quine-relay.scm 8 64 1956 total
動機
「SchemeやRubyが得意でHaskellやOCamlも書けます!」と堂々と言えるようになりたい。ならばそれらの言語でQuineリレーをしよう。思い付いてしまったからには、書かねばなるまい。そう思っていた時期もあった。
かのように決心して書き始めたのだが、存外に難しくはなく、その日のうちにできてしまった。
構想
コンセプト。Quineリレー自体は偉大なる先人が凄いのを成し遂げていらっしゃるし、自分はQuineリレーには初挑戦なので、今回は得意言語のアッピルに重点を置いて、とりあえず高級言語数個での完成を目指すことにした。次に順序が問題となる。Brainf**kやBefungeを入れるでもないのに順序が任意では趣に欠けるというもの。これはなんとなくCを起点にして触った順にした。結果、自分のプログラミングの学びの歴史を辿り最後は初心に帰るという個人的に気持ち悪い大層趣のあるプログラムができあがった。あと、今回の主旨に鑑みて、特定の言語で何重にもエスケープして残りの言語は文字列を出力するだけ、みたいなヒキョウなことはなるべくしないようにした。
もう一つ、追加の制約としてCコンパイラのオプションに -std=c99 -Wall -W -Werror -pedantic
を付けることにした。これでこのプログラムがコンパイラも黙る程の真っ当なプログラムであることは確定的に明らか。奇妙な挙動や拡張に依存していたら、コンパイラによってバラバラに引き裂かれることになる。
ついでにちょっと縮めた。
感想
付録
.PHONY: all clean diff CC = clang CFLAGS = -std=c99 -Wall -W -Werror -pedantic JavaScript = v8 --use_strict Ruby = ruby Scheme = gosh OCaml = ocaml Haskell = runghc DIFF = diff -u START = start.c GOAL = goal.c Q = quine-relay C_EXE = $(Q)-c all: $(GOAL) diff: all $(DIFF) $(START) $(GOAL) $(Q).js: $(START) $(CC) $(CFLAGS) -o $(C_EXE) $< && ./$(C_EXE) > $@ $(Q).rb: $(Q).js $(JavaScript) $< > $@ $(Q).scm: $(Q).rb $(Ruby) $< > $@ $(Q).ml: $(Q).scm $(Scheme) $< > $@ $(Q).hs: $(Q).ml $(OCaml) $< > $@ $(GOAL): $(Q).hs $(Haskell) $< > $@ clean: rm -f $(GOAL) $(C_EXE) $(Q).js $(Q).rb $(Q).scm $(Q).ml $(Q).hs
あと各処理系のバージョン。Gaucheは開発版の何か。
$ clang -v clang version 3.5.0 (tags/RELEASE_350/final) Target: x86_64-apple-darwin13.4.0 Thread model: posix $ echo -n | v8 V8 version 3.25.30 [sample shell] > $ ruby -v ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin13] $ gosh -V Gauche scheme shell, version 0.9.5_pre1 [utf-8,pthreads], x86_64-apple-darwin13.4.0 $ ocaml -version The OCaml toplevel, version 4.02.0 $ ghc -V The Glorious Glasgow Haskell Compilation System, version 7.8.3