microKanrenで始めるlogic programming入門 (2)

単一化しようぜ

==の動作を見てみよう。

(define (== u v)
  (lambda (s/c)
    (let ((s (unify u v (car s/c))))
      (if s (unit `(,s . ,(cdr s/c))) mzero))))

前にも説明したように(== u v)はuとvが同一であることを宣言している。この時==は引数のstateであるs/cを拡張してuとvが同じ値になるような代入を持つstateを作る関数を生成する。実際にある代入sの下でuとvが同じ値になるような代入を探すのがunifyだ。

(define (unify u v s)
  (let ((u (walk u s)) (v (walk v s)))
    (cond
      ((and (var? u) (var? v) (var=? u v)) s)
      ((var? u) (ext-s u v s))
      ((var? v) (ext-s v u s))
      ((and (pair? u) (pair? v))
       (let ((s (unify (car u) (car v) s)))
         (and s (unify (cdr u) (cdr v) s))))
      (else (and (eqv? u v) s)))))

unifyの動作例を並べてみる。

(unify #(0) 3 ())
; => ((#(0) . 3))

(unify 3 #(1) ())
; => ((#(1) . 3))

(unify 4 4 ())
; => ()

(unify 2 4 ()
; 単一化に失敗すると#fを返す
; => #f

(unify #(0) #(1) '((#(0) . 1)))
; => ((#(1) . 1) (#(0) . 1))

(unify #(0) 2 '((#(0) . 1)))
; 代入と矛盾してるので失敗
; => #f

(unify (list #(0) 2) (list 3 #(1)) mzero)
; リスト同士の単一化もできる
; => ((#(1) . 2) (#(0) . 3))