EoPL reading (81) 2.2 An Abstraction for Inductive Data Type

検討再開。とりあえず fresh-id と all-ids がポイント高い、という事で例示されている手続きを吸わせてみる事に。

gosh> (all-ids '(lambda-exp p (app-exp (var-exp +) (app-exp (var-exp p) (var-exp x)))))
(p + x)
gosh> 

ええと、

(lambda (p) (+ p x))

で、x を (* p 3) で置き換えるので、subst-id が x で subst-exp が (* p 3) になる。てーことは、subst-exp と上記のリストで重複してるシンボルが fresh-id する対象になる、という事で良いの??
なんとなく悩ましいんですが

  • all-ids は lit-exp と priapp-exp を追加した形にしたい
    • そしたら上記リストは (p x) になりますな
    • ついでに subst-id と同値のものはナニしたい
  • all-ids で一覧作って fresh-id するのは微妙
    • all-ids 何度も呼ぶのは (ry
  • 置き換えなナニは alist で (ry

ってコトでとりあえず all-ids を以下に

(add-load-path ".")
(load "define-datatype")

(define all-ids
  (lambda (exp)
    (let f ((rslt '()) (exp exp))
      (cases expression exp
	     (var-exp (id) (if (memv id rslt)
			       rslt
			       (append rslt (list id))))
	     (lit-exp (datum) rslt)
	     (primapp-exp (prim rand1 rand2)
			  (f (f rslt rand1) rand2))
	     (lambda-exp (id body)
			 (f (append rslt (list id)) body))
	     (app-exp (rator rand)
		      (f (f rslt rator) rand))))))

試験もコピーして以下を追加。

(test* "all-ids (5)"
       '(x)
       (all-ids '(lambda-exp x (primapp-exp + (var-exp x) (lit-exp 3)))))

パス。最初 (app-exp (primapp-exp +) (app-exp (var-exp x) (lit-exp 3))) とか書いてて微妙にハマってたり。
で、色々ごにょごにょしつつ以下がでっち上がった。

(add-load-path ".")
(load "expression")
(load "fresh-id")

(define make-subst-alist
  (lambda (ids l)
    (let f ((rslt '()) (ids ids))
      (if (null? ids)
	  rslt
	  (f (append rslt 
		     (list (list (car ids) 
				 (fresh-id l (symbol->string (car ids))))))
	     (cdr ids))))))

(define rename-or-not
  (lambda (id alist)
    (let ((s (assv id alist)))
      (if s
	  (cadr s)
	  id))))

(define lambda-calculus-subst
  (lambda (exp subst-exp subst-id)
    (let ((rename-ids (all-ids exp)))
      (let ((subst-alist (make-subst-alist rename-ids exp)))
	(letrec
	    ((subst
	      (lambda (exp)
		(cases expression exp
		       (var-exp (id)
				(if (eqv? id subst-id) 
				    subst-exp 
				    (var-exp (rename-or-not id subst-alist))))
		       (lambda-exp (id body)
				   (lambda-exp (rename-or-not id subst-alist)
					       (subst body)))
		       (app-exp (rator rand)
				(app-exp (subst rator) (subst rand)))
		       (lit-exp (datum)
				(lit-exp datum))
		       (primapp-exp (prim rand1 rand2)
				    (primapp-exp prim
						(subst rand1)
						(subst rand2)))))))
	  (subst exp))))))

実は上記はダウト。以下な試験を作ってて

(use gauche.test)

(add-load-path ".")
(load "lambda-calculus-subst")

(test-start "make-subst-alist")
(test-section "make-subst-alist")
(test* "make-subst-alist (1)"
       '()
       (make-subst-alist '() '()))
(test* "make-subst-alist (2)"
       '((x x0))
       (make-subst-alist '(x) '(lambda-exp x (var-exp x))))
(test* "make-subst-alist (3)"
       '()
       (make-subst-alist '(x) '(lambda-exp p (var-exp p))))

(test-end)

パスしません。

$ gosh test-lambda-calculus-subst.scm
Testing lambda-calculus-subst ...                                
<lambda-calculus-subst>--------------------------------------------------------
test lambda-calculus-subst (1), expects (lambda-exp p0 (primapp-exp + (var-exp p0) (primapp-exp * (var-exp p) (lit-exp 3)))) ==> ok
test lambda-calculus-subst (2), expects (lambda-exp q (primapp-exp + (var-exp q) (primapp-exp * (var-exp p) (lit-exp 3)))) ==> ERROR: GOT (lambda-exp q0 (primapp-exp + (var-exp q0) (primapp-exp * (var-exp p) (lit-exp 3))))
failed.
discrepancies found.  Errors are:
test lambda-calculus-subst (2): expects (lambda-exp q (primapp-exp + (var-exp q) (primapp-exp * (var-exp p) (lit-exp 3)))) => got (lambda-exp q0 (primapp-exp + (var-exp q0) (primapp-exp * (var-exp p) (lit-exp 3))))

なんか長いですが

(lambda (q) (+ q x))

(lambda (q0) (+ q0 (* p 3)))

になってます。むむむ、と言いつつ上記の下書きをニラんでて勘違いに気づきました。lambda-calculus-subst 手続きは正しくは以下。

(define lambda-calculus-subst
  (lambda (exp subst-exp subst-id)
;;    (let ((rename-ids (all-ids exp)))
    (let ((rename-ids (all-ids subst-exp)))
      (let ((subst-alist (make-subst-alist rename-ids exp)))
	(letrec
	    ((subst
	      (lambda (exp)
		(cases expression exp
		       (var-exp (id)
				(if (eqv? id subst-id) 
				    subst-exp 
				    (var-exp (rename-or-not id subst-alist))))
		       (lambda-exp (id body)
				   (lambda-exp (rename-or-not id subst-alist)
					       (subst body)))
		       (app-exp (rator rand)
				(app-exp (subst rator) (subst rand)))
		       (lit-exp (datum)
				(lit-exp datum))
		       (primapp-exp (prim rand1 rand2)
				    (primapp-exp prim
						(subst rand1)
						(subst rand2)))))))
	  (subst exp))))))

これで試験にパスしたんですが、なんとなく腑にオチてません。あんまシンプルではないなぁ、と。