2019/01/16
昨日泥酔してたのに、意外と午後からは回復した。
相思相愛ロリータのサントラ届いた。
4年前のエロゲに囚われている。
お泊まり恋人ロリータだけまだやってない気がする。
相思相愛、もういちどやろうとおもったけど、どのサイトで購入したのかわからなくてめっちゃ探した結果、dl.getchu.comで買っていたということが判明した。
akashi:/data/文書/games/夜のひつじ に置いておきました。
GCいろいろ捨てた。
1500円/営業日つみたて、しんどくなってきたので、300円/営業日にまで落とした。
楽天銀行のランクを最高に保つには、1日2件程度のつみたてはしないといけない。
いまさらSICP読書会の前回の最後サボったところを実装。
練習問題4.5 eval
scheme
(cond ((begin (print 42) true) => id))
のようなコードを実行すると、42は一度しか表示されないので、素直に実装すると 、
wrong.scm
(if (begin (print 42) true) (id (begin (print 42) true)) false)
のようになってしまうと思うが、これは誤りで、
true.scm
(let ((t (begin (print 42) true))) (if t (id t) false))
と変換しなければならないのではないかと思う。
これを主張しているのが、https://kmc.hatenablog.jp/entry/2019/01/11/011314 の1つ目の写真。
ついでに後ろの4.6でletの脱糖が見えているので同時に行ったのが以下。
my.scm
(define (make-cond-arrow first rest)
; (cond ((begin (print 42) #t) => id)
; は42を出力するので、
; (if (begin (print 42) #t) (id (begin (print 42) #t)) #f)
; のように変換するのではなく、
; (let ((t (begin (print 42) #t))) (if t (id t) #f))
; のように変換する必要がある。
;; https://kmc.hatenablog.jp/entry/2019/01/11/011314 の最初の写真
(make-let `((t ; restの内側で使われている名前と衝突するとマズい気がする。
,(cond-arrow-operand first)))
(make-if
't
(list (cond-arrow-operator first) 't)
rest)))
(define (cond->if exp) (expand-clauses (cond-clauses exp)))
(define (expand-clauses clauses)
(if (null? clauses)
'false ; else節はない
(let ((first (car clauses))
(rest (cdr clauses)))
(cond ((cond-else-clause? first)
(if (null? rest)
(sequence->exp (cond-actions first))
(error "ELSE clause isn't last: COND->IF" clauses)))
((cond-arrow-clause? first)
(make-cond-arrow
first
(expand-clauses rest)))
(else
(make-if (cond-predicate first)
(sequence->exp (cond-actions first))
(expand-clauses rest)))))))
等を見ると、このへんが考慮されていないような解答に見えるが、これを考慮してこの段階でこの
=> cond
を実装するの、かなり難しくない? もちろん
cond->if
をあきらめて、 eval-cond
するような実装はできるし、実際それっぽいのは出てきた。 今のところ↑のコードだと、以下のように最終的に
lambda
へと落ちるので make-procedure
等の実装が無い現時点では動かない気がする……。 とりあえず上記のコードでは、元々schemeにある
eval
を呼んでいるが……。cond->if.scm
gosh> (cond->if '(cond ((begin (print 42) true) => id)))
((lambda (t) (if t (id t) false)) (begin (print 42) true))
scheme
(make-let `((t ; restの内側で使われている名前と衝突するとマズい気がする。
,(cond-arrow-operand first)))
あともう1つ、
let
に変換してやる際、名前の衝突が問題になる気がする。 この場合、ここより外にある
t
を cond
の中で読んでいるとおかしなことになりそう。 (eval '(let ((t 42)) (cond ((assoc 'b '((a 1) (b 2))) => (lambda(x) t)) (else false))) '())
とかで問題になるかと思ったけどならなかった。 なんで?
let
が先に脱糖されて ((lambda (t) (cond ((assoc 'b '((a 1) (b 2))) => (lambda (x) t)) (else false))) 42)
になるので。 とおもったけどこれでは問題は解決していない気がする。
この後は
eval
されて lambda
にひっかかってoriginalの eval
が動いているので、問題になっていないだけではないか。cond->if.scm
gosh> (cond->if '(cond ((assoc 'b '((a 1) (b 2))) => (lambda(x) t)) (else false)))
((lambda (t) (if t ((lambda (x) t) t) false)) (assoc 'b '((a 1) (b 2))))
手で脱糖するとこうなり、これを↑に代入すると、
((lambda (t) (((lambda (t) (if t ((lambda (x) t) t) false)) (assoc 'b '((a 1) (b 2)))))) 42)
こう。 これは明らかに
t
の混同が発生してそうで、実行すると invalid application: ((b 2))
となる。 (let ((t 42)) (cond ((assoc 'b '((a 1) (b 2))) => (lambda(x) t)) (else false)))
は42が帰ってきて欲しいはず。 実用的なLispシステムでは、ユーザーが新しい派生式を追加し、評価器を修正することなく構文変形としての実装を規定できる仕組みを提供しています。そのようなユーザー定義の変形はマクロ(macro)と呼ばれます。単純なマクロ定義の仕組みを追加することは簡単なのですが、そうすると言語が名前の衝突という厄介な問題を持つことになります。
4.1.2 式の表現 > 派生式 > 注13(手元ではP407)
これになっている……?
朝になって日付が変わったので寝ましょう。
日付変更は朝の5時。