EoPL reading (128) 2.3 Representation Strategies for Data Types

小出しになってしまってますがご容赦下さい。続きです。
t が var-term 以外のケイスを以下に。

  • u が var-term の場合、引数を入れ替えて unify-term 呼び出し
  • u が constant-term の場合
    • t が constant-term で値が同一なら (empty-subst) を戻す
  • u が app-term の場合
    • t が app-term なら unify-terms に app-term の中身を渡して戻りを戻す

試験が以下ですが、unify-terms が #f を戻すケイスもあるな。ちょっと unify-terms 分についてここではオミットします。

(test* "(unify-term '(constant-term 1) '(var-term x))"
       (extend-subst 'x (constant-term 1) (empty-subst))
       (unify-term '(constant-term 1) '(var-term x)))
(test* "(unify-term '(constant-term 1) '(constant-term 1))"
       (empty-subst)
       (unify-term '(constant-term 1) '(constant-term 1)))

ここからがある意味本番か。とりあえず unify-terms が #f 戻すケイスを列挙してみる。

  • リストの流さが合ってない場合
  • car 要素を unify-term した結果が #f の場合
  • cdr 要素を unify-terms した結果が #f の場合

最後の試験はこれだけで良いのかどうか微妙ですが

(test* "(unify-terms '() '(var-term a))"
       #f
       (unify-terms '() '(var-term a)))

(test* "(unify-terms '(var-term a) '())"
       #f
       (unify-terms '(var-term a) '()))

(test* "(unify-terms '((var-term x)) '((app-term ((var-term x)))))"
       #f
       (unify-terms '((var-term x)) '((app-term ((var-term x))))))

(test* "(unify-terms '((var-term a) (var-term b)) '((var-term c)))"
       #f
       (unify-terms '((var-term a) (var-term b)) '((var-term c))))

で、ここまで書いた時点で compose-subst 実装してないコトに気づく。基本的な仕様としては

(define compose-subst
  (lambda (subst-car subst-cdr)

の subst-car の末端の substitution が subst-cdr になれば良いはず。

    (cases substitution subst-car
           (empty-subst-rec ()
                            (empty-subst))
           (extended-subst-rec (id term sub)
                               (extend-subst id term subst-cdr)))))

というカンジかなぁ。試験が以下。

(test-section "compose-subst")
(test* "(compose-subst (empty-subst) (empty-subst))"
       (empty-subst)
       (compose-subst (empty-subst) (empty-subst)))
(test* "(compose-subst (extend-subst x (var-term y) (empty-subst)) (extend-subst y (constant-term 1) (empty-subst)))"
       (extend-subst 'x (var-term 'y) (extend-subst 'y (constant-term 1) (empty-subst)))
       (compose-subst (extend-subst 'x (var-term 'y) (empty-subst)) 
		      (extend-subst 'y (constant-term 1) (empty-subst))))

うーむ。本当に良いのだろうか。とりあえずここまで来たら確認はもう少しだな。ただ、以下の処理の検証とかまだあまりきちんとできてない。

		 (let ((new-ts (subst-in-terms (cdr ts) subst-car))
		       (new-us (subst-in-terms (cdr us) subst-car)))

とりあえず単純なヤツから。

(test* "(unify-terms '((var-term a) (var-term b)) '((constant-term 1) (constant-term 2)))"
       (extend-subst 'a 
		     (constant-term 1)
		     (extend-subst 'b (constant-term 2) (empty-subst)))
       (unify-terms '((var-term a) (var-term b)) 
		    '((constant-term 1) (constant-term 2))))

ソースが

			 (compose-substs subst-car subst-cdr))))))))))

ってなってた。compose-subst に修正したら試験パス。

再開

理解が微妙なのが

  • unify-term で memv してるナニ
  • unify-terms で cdr 要素について subst-in-terms してるソレ

ちょっと集中して検討してみます。まず最初のナニですが

    (cases term t
	   (var-term (tid)
		     (if (or (var-term? u) (not (memv tid (all-ids u))))
			 (unit-subst tid u)

という事で t が var-term で u が app-term なケイス限定なのかな。例えば

(unify-term '(var-term x) '(app-term ((var-term y))))

みたいなカンジ。これは x という変数が (y) という手続き呼び出しに置き替えなナニ。これって確かに x が (x) に、というのは微妙なのか。あるいは

(unify-term '(var-term x) '(app-term ((var-term y) (var-term x))))

というケイス。これは x が (y x) に変換。なんとなく再帰ループを連想してしまう。のでチェックしてるのだろうな、という事なんですがダウトでしょうか。
次。cdr を変換しているのは何故か、という事なんですが、サンプルとしてどんな式があり得るのかな。例えば以下?

((var-term x) (var-term y) (var-term y))

((var-term z) (constant-term 1) (constant-term 1))

を unify-terms に渡すとすると最初に subst-car に格納されるのは

(extend-subst x (var-term z) (empty-subst))

で、これは new-ts とか new-us を生成するナニには影響はしないはず。うん、しないな。イメージ的には次だな。

((var-term y) (var-term y))

((constant-term 1) (constant-term 1))

になります。これは

(extend-subst y (constant-term 1) (empty-subst))

という substitution を生成するんですが、これを cdr に適用する事で ts 側は

((constant-term 1))

になって us 側はそのまんまの

((constant-term 1))

になってこの car を unify-term に渡すと (empty-subst) が戻りますな。でもなんとなくオチてないな。ただ、unify-terms って

(x y y)

という式を

(z 1 1)

という式に置き換えるという事なのか。もう少し複雑な式をイメージした方が良いのかな。例えば以下とか?

(x (x 1 2) (x 3 4))

あ、unify-terms とか unify-term って substitution なオブジェクトのコンストラクタなのか。ここまでのソレを試験にしてみて終わり、で良い気がしてます。とりあえず試験を作りましょう。

(test* "(unify-terms '((var-term x) (var-term y) (var-term y)) '((var-term z) (constant-term 1) (constant-term 1)))"
       (extend-subst 'x
		     (var-term 'z)
		     (extend-subst 'y (constant-term 1) (empty-subst)))
       (unify-terms '((var-term x) (var-term y) (var-term y)) '((var-term z) (constant-term 1) (constant-term 1))))

これで OK ってコトにします。実装と試験を以下に。
まず実装。# substitution なソレのみ

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

(define-datatype substitution substitution?
  (empty-subst-rec)
  (extended-subst-rec
   (id symbol?)
   (term term?)
   (sub substitution?)))		 

(define empty-subst
  (lambda ()
    (empty-subst-rec)))

(define extend-subst
  (lambda (i t s)
    (extended-subst-rec i t s)))

(define apply-subst
  (lambda (s i)
    (cases substitution s
	   (empty-subst-rec ()
			    (var-term i))
	   (extended-subst-rec (id term sub)
			       (if (eqv? i id)
				   term
				   (apply-subst sub i))))))

(define subst-in-term
  (lambda (t s)
    (cases term t
	   (var-term (id) (apply-subst s id))
	   (constant-term (datum) (constant-term datum))
	   (app-term (terms)
		     (let f ((rslt '()) (terms terms))
		       (if (null? terms)
			   (app-term rslt)
			   (f (append rslt (list (subst-in-term (car terms) s)))
			      (cdr terms))))))))

(define subst-in-terms
  (lambda (l s)
    (map (lambda (t) (subst-in-term t s)) l)))

(define unit-subst
  (lambda (id term)
    (extend-subst id term (empty-subst))))

(define var-term?
  (lambda (t)
    (cases term t
      (var-term (id) #t)
      (else #f))))

(define all-ids
  (lambda (t)
    (define all-ids-inner
      (lambda (ret t)
	(cases term t
	       (constant-term (datum) ret)
	       (var-term (id) (if (memv id ret)
				  ret
				  (append ret (list id))))
	       (app-term (terms)
			 (let f ((ret ret) (terms terms))
			   (if (null? terms)
			       ret
			       (f (all-ids-inner ret (car terms)) (cdr terms))))))))
    (all-ids-inner '() t)))

(define compose-subst
  (lambda (s-car s-cdr)
    (cases substitution s-car
	   (empty-subst-rec ()
			    (empty-subst))
	   (extended-subst-rec (id term sub)
			       (extend-subst id term s-cdr)))))

(define unify-term
  (lambda (t u)
    (cases term t
	   (var-term (tid)
		     (if (or (var-term? u) (not (memv tid (all-ids u))))
			 (unit-subst tid u)
			 #f))
	   (else
	    (cases term u
		   (var-term (uid) (unify-term u t))
		   (constant-term (udatum)
				  (cases term t
					 (constant-term 
					  (tdatum)
					  (if (equal? tdatum udatum)
					      (empty-subst)
					      #f))
					 (else #f)))
		   (app-term (us)
			     (cases term t
				    (app-term (ts) (unify-terms ts us))
				    (else #f))))))))

(define unify-terms
  (lambda (ts us)
    (cond ((and (null? ts) (null? us)) (empty-subst))
	  ((or (null? ts) (null? us)) #f)
	  (else
	   (let ((subst-car (unify-term (car ts) (car us))))
	     (if (not subst-car)
		 #f
		 (let ((new-ts (subst-in-terms (cdr ts) subst-car))
		       (new-us (subst-in-terms (cdr us) subst-car)))
		   (let ((subst-cdr (unify-terms new-ts new-us)))
		     (if (not subst-cdr)
			 #f
			 (compose-subst subst-car subst-cdr))))))))))

次が試験。これも 2.25 マターなソレだけ。

(use gauche.test)

(add-load-path ".")
(load "substitution")

(test-start "ex.2.25")
(test-section "var-term?")
(test* "(var-term? '(var-term a))"
       #t
       (var-term? '(var-term a)))
(test* "(var-term? '(constant-term 1))"
       #f
       (var-term? '(constant-term 1)))
(test* "(var-term? '(app-term ((var-term x) (var-term y))))"
       #f
       (var-term? '(app-term ((var-term x) (var-term y)))))

(test-section "all-ids")
(test* "(all-ids '(constant-term 1)) returns"
       '()
       (all-ids '(constant-term 1)))
(test* "(all-ids '(var-term a)) returns"
       '(a)
       (all-ids '(var-term a)))
(test* "(all-ids '(app-term ((constant-term 1) (constant-term 2)))) returns"
       '()
       (all-ids '(app-term ((constant-term 1) (constant-term 2)))))
(test* "(all-ids '(app-term ((var-term a) (var-term a)))) returns"
       '(a)
       (all-ids '(app-term ((var-term a) (var-term a)))))
(test* "(all-ids '(app-term ((app-term ((var-term a) (var-term b))) (var-term c)))) returns"
       '(a b c)
       (all-ids '(app-term ((app-term ((var-term a) (var-term b))) (var-term c)))))

(test-section "unify-term")
(test* "(unify-term (var-term a) (var-term x)) returns"
       '(extended-subst-rec a (var-term x) (empty-subst-rec))
       (unify-term '(var-term a) '(var-term x)))

(test* "(unify-term (var-term a) (constant-term 1)) returns"
       '(extended-subst-rec a (constant-term 1) (empty-subst-rec))
       (unify-term '(var-term a) '(constant-term 1)))

(test* "(unify-term (var-term a) (var-term a)) returns"
       '(extended-subst-rec a (var-term a) (empty-subst-rec))
       (unify-term '(var-term a) '(var-term a)))

(test* "(unify-term (var-term x) (app-term ((var-term y) (constant-term 1) (constant-term 2)))) returns"
       '(extended-subst-rec x (app-term ((var-term y) (constant-term 1) (constant-term 2))) (empty-subst-rec))
       (unify-term '(var-term x) '(app-term ((var-term y) (constant-term 1) (constant-term 2)))))

(test* "(unify-term (var-term x) (app-term ((var-term x) (constant-term 1) (constant-term 2)))) returns"
       #f
       (unify-term '(var-term x) '(app-term ((var-term x) (constant-term 1) (constant-term 2)))))

(test* "(unify-term ’(constant-term 1) ’(var-term x)) returns"
       '(extended-subst-rec x (constant-term 1) (empty-subst-rec))
       (unify-term '(constant-term 1) '(var-term x)))

(test* "(unify-term ’(app-term ((var-term x) (constant-term 1))) ’(var-term x)) returns"
       #f
       (unify-term '(app-term ((var-term x) (constant-term 1))) '(var-term x)))

(test* "(unify-term ’(app-term ((var-term y) (constant-term 1))) ’(var-term x)) returns"
       '(extended-subst-rec x (app-term ((var-term y) (constant-term 1))) (empty-subst-rec))
       (unify-term '(app-term ((var-term y) (constant-term 1))) '(var-term x)))

(test* "(unify-term ’(constant-term 1) ’(constant-term 1)) retuens"
       '(empty-subst-rec)
       (unify-term '(constant-term 1) '(constant-term 1)))

(test* "(unify-term ’(constant-term 1) ’(constant-term 2)) returns"
       #f
       (unify-term '(constant-term 1) '(constant-term 2)))

(test* "(unify-term ’(constant-term 1) ’(app-term ((var-term x) (constant-term 1)))) returns"
       #f
       (unify-term '(constant-term 1) '(app-term ((var-term x) (constant-term 1)))))

(test-section "unify-term (2)")
(test* "(unify-term '(var-term x) '(app-term ((var-term x))))"
       #f
       (unify-term '(var-term x) '(app-term ((var-term x)))))
(test* "(unify-term '(constant-term 1) '(app-term ((var-term x))))"
       #f
       (unify-term '(constant-term 1) '(app-term ((var-term x)))))
(test* "(unify-term '(constant-term 1) '(constant-term 2))"
       #f
       (unify-term '(constant-term 1) '(constant-term 2)))
(test* "(unify-term '(app-term ((var-term x))) '(constant-term 1))"
       #f
       (unify-term '(app-term ((var-term x))) '(constant-term 1)))
(test* "(unify-term '(app-term ((var-term x))) '(var-term x))"
       #f
       (unify-term '(app-term ((var-term x))) '(var-term x)))

(test* "(unify-term '(var-term x) '(var-term y))"
       (extend-subst 'x (var-term 'y) (empty-subst))
       (unify-term '(var-term x) '(var-term y)))
(test* "(unify-term '(var-term x) '(constant-term 1))"
       (extend-subst 'x (constant-term 1) (empty-subst))
       (unify-term '(var-term x) '(constant-term 1)))
(test* "(unify-term '(var-term x) '(app-term ((var-term y))))"
       (extend-subst 'x '(app-term ((var-term y))) (empty-subst))
       (unify-term '(var-term x) '(app-term ((var-term y)))))

(test* "(unify-term '(constant-term 1) '(var-term x))"
       (extend-subst 'x (constant-term 1) (empty-subst))
       (unify-term '(constant-term 1) '(var-term x)))
(test* "(unify-term '(constant-term 1) '(constant-term 1))"
       (empty-subst)
       (unify-term '(constant-term 1) '(constant-term 1)))

(test-section "unify-terms")
(test* "(unify-terms '() '())"
       '(empty-subst-rec)
       (unify-terms '() '()))

(test* "(unify-terms '() '(var-term a))"
       #f
       (unify-terms '() '(var-term a)))

(test* "(unify-terms '(var-term a) '())"
       #f
       (unify-terms '(var-term a) '()))

(test* "(unify-terms '((var-term x)) '((app-term ((var-term x)))))"
       #f
       (unify-terms '((var-term x)) '((app-term ((var-term x))))))

(test* "(unify-terms '((var-term a) (var-term b)) '((var-term c)))"
       #f
       (unify-terms '((var-term a) (var-term b)) '((var-term c))))

(test* "(unify-terms '((var-term a) (var-term b)) '((constant-term 1) (constant-term 2)))"
       (extend-subst 'a 
		     (constant-term 1)
		     (extend-subst 'b (constant-term 2) (empty-subst)))
       (unify-terms '((var-term a) (var-term b)) 
		    '((constant-term 1) (constant-term 2))))

(test* "(unify-terms '((var-term x) (var-term y) (var-term y)) '((var-term z) (constant-term 1) (constant-term 1)))"
       (extend-subst 'x
		     (var-term 'z)
		     (extend-subst 'y (constant-term 1) (empty-subst)))
       (unify-terms '((var-term x) (var-term y) (var-term y)) '((var-term z) (constant-term 1) (constant-term 1))))

(test-section "compose-subst")
(test* "(compose-subst (empty-subst) (empty-subst))"
       (empty-subst)
       (compose-subst (empty-subst) (empty-subst)))
(test* "(compose-subst (extend-subst x (var-term y) (empty-subst)) (extend-subst y (constant-term 1) (empty-subst)))"
       (extend-subst 'x (var-term 'y) (extend-subst 'y (constant-term 1) (empty-subst)))
       (compose-subst (extend-subst 'x (var-term 'y) (empty-subst)) 
		      (extend-subst 'y (constant-term 1) (empty-subst))))

(test-end)

今から 2.4 節を見ます。