## クワインで遊ぶ
### 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