## クワインで遊ぶ ### nz_tcoder --- ## 目次 * クワインとは? * lispの例 * Cの例 * C → lisp * 考察 --- ## クワインとは? ウィキペディアより引用 ``` クワイン(英: Quine)は、コンピュータプログラムの一種で、 自身のソースコードと完全に同じ文字列を出力するプログラムである。 ``` scheme/common lisp ```lisp CL-USER> ((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x)))) ((LAMBDA (X) (LIST X (LIST 'QUOTE X))) '(LAMBDA (X) (LIST X (LIST 'QUOTE X)))) CL-USER> (equal * +) T ``` --- ## lispの例 バッククォートで書き直す ```lisp ((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x)))) ((lambda (x) `(,x ',x)) '(lambda (x) `(,x ',x))) ``` 動作確認 ```lisp CL-USER> ((lambda (x) `(,x ',x)) '(lambda (x) `(,x ',x))) ((LAMBDA (X) `(,X ',X)) '(LAMBDA (X) `(,X ',X))) CL-USER> (equal * +) T ``` --- ### バッククォートの出力は処理依存 ccl ```lisp ? ((lambda (x) `(,x ',x)) '(lambda (x) `(,x ',x))) ((LAMBDA (X) (LIST* X (LIST (LIST* 'QUOTE (LIST X))))) '(LAMBDA (X) (LIST* X (LIST (LIST* 'QUOTE (LIST X)))))) ? (equal * +) T ``` --- ## lispの例 `lambda`→`let` ```lisp CL-USER> ((lambda (x) `(,x ',x)) '(lambda (x) `(,x ',x))) ((LAMBDA (X) `(,X ',X)) '(LAMBDA (X) `(,X ',X))) CL-USER> (let ((x '(lambda (x) `(,x ',x)))) ; letを使って同じ出力にする `(,x ',x)) ((LAMBDA (X) `(,X ',X)) '(LAMBDA (X) `(,X ',X))) CL-USER> (let ((x '(lambda (x) `(,x ',x)))) `(let ((x ,x)) ',x)) ; letを挿入 (LET ((X (LAMBDA (X) `(,X ',X)))) '(LAMBDA (X) `(,X ',X))) ; -> 頭のquoteが邪魔 CL-USER> (let ((x '(lambda (x) `(,x ',x)))) `(let ((x ',x)) ,x)) ; quoteの位置を入れ替える (LET ((X '(LAMBDA (X) `(,X ',X)))) (LAMBDA (X) `(,X ',X))) ``` --- ## lispの例 `let`版 ``` (lambda (x) `(,x ',x)) を `(let ((x ',x)) ,x) に 置換 ``` ``` (let ((x '(lambda (x) `(,x ',x)))) `(let ((x ',x)) ,x)) CL-USER> (let ((x '`(let ((x ',x)) ,x))) `(let ((x ',x)) ,x)) (LET ((X '`(LET ((X ',X)) ,X))) `(LET ((X ',X)) ,X)) CL-USER> (equal * +) T ``` --- ## lispの例 ```lisp (let ((x '`(let ((x ',x)) ,x))) `(let ((x ',x)) ,x)) ``` xである必要はない。yでもzでも… x → let ```lisp CL-USER> (let ((let '`(let ((let ',let)) ,let))) `(let ((let ',let)) ,let)) (LET ((LET '`(LET ((LET ',LET)) ,LET))) `(LET ((LET ',LET)) ,LET)) CL-USER> (equal * +) T ``` --- ## xである必要はない。 ```lisp ((lambda (x) `(,x ',x)) '(lambda (x) `(,x ',x))) ``` x → lambda ```lisp CL-USER> ((lambda (lambda) `(,lambda ',lambda)) '(lambda (lambda) `(,lambda ',lambda))) ((LAMBDA (LAMBDA) `(,LAMBDA ',LAMBDA)) '(LAMBDA (LAMBDA) `(,LAMBDA ',LAMBDA))) CL-USER> (equal * +) T ``` --- ## Cの例 ウィキペディアより(実際は一行) ```C int main() { char *s = "int main() { char *s = %c%s%c; printf(s, 34, s, 34); }"; printf(s, 34, s, 34); } ``` コンパイルと実行 ``` $ cat quine.c int main() { char *s = "int main() { char *s = %c%s%c; printf(s, 34, s, 34); }"; printf(s, 34, s, 34); }$ $gcc -o quine quine.c quine.c: In function ‘main’: quine.c:1: warning: incompatible implicit declaration of built-in function ‘printf’ $ ./quine > quine.out $ cat quine.out int main() { char *s = "int main() { char *s = %c%s%c; printf(s, 34, s, 34); }"; printf(s, 34, s, 34); }$ $ cmp quine.c quine.out $ ``` --- ## C → lisp `printf`を使って書けるなら、`format`を使っても書けるはず。 ```C int main() { char *s = "int main() { char *s = %c%s%c; printf(s, 34, s, 34); }"; printf(s, 34, s, 34); } ``` ```lisp CL-USER> (let ((s "(let ((s ~s)) (format t s s))")) (format t s s)) (let ((s "(let ((s ~s)) (format t s s))")) (format t s s)) NIL ``` `~s` は文字列はダブルクォート付で出力する lispなのでS式を出力するようにしてみる。 --- ## C → lisp ```lisp (let ((s "(let ((s ~s)) (format t s s))")) (format t s s)) CL-USER> (let ((s "(let ((s ~s)) (read-from-string (format nil s s)))")) (read-from-string (format nil s s))) (LET ((S "(let ((s ~s)) (read-from-string (format nil s s)))")) (READ-FROM-STRING (FORMAT NIL S S))) 100 CL-USER> (equal * +) T ``` `read-from-string`はobjectとポジションを返す。 変数なしで出来ないか? --- ## C → lisp 文字列出力版 ```lisp CL-USER> (format t "(format t ~s ~:*~s)" "(format t ~s ~:*~s)") (format t "(format t ~s ~:*~s)" "(format t ~s ~:*~s)") NIL ``` `~:*` は一度消費した引数を再度使えるようにする。 --- ## C → lisp 変数なし(S式出力版) ```lisp CL-USER> (read-from-string (format nil "(read-from-string (format nil ~s ~:*~s))" "(read-from-string (format nil ~s ~:*~s))")) (READ-FROM-STRING (FORMAT NIL "(read-from-string (format nil ~s ~:*~s))" "(read-from-string (format nil ~s ~:*~s))")) 117 CL-USER> (equal * +) T ``` --- ## 考察 クワインはevalについての不動点である。 ```lisp CL-USER> (setq p '((lambda (x) (list x (list 'quote x))) '(lambda (x) (list x (list 'quote x))))) CL-USER> (equal p (eval p)) T CL-USER> ``` Cでは文字列との格闘。lispでは文字列との格闘はほとんどない。 --- ## 参考文献 1. ウィキペディア クワイン (プログラミング) 1. Let Over Lambda(Doug Hoyte著,エスアイビーアクセス社)
(英語版 https://letoverlambda.com/ ) 1. 実践Common Lisp(Peter Seibel著,オーム社)
(英語版 http://www.gigamonkeys.com/book/ ) 1. The Quine Page,http://www.nyx.net/~gthompso/quine.htm