マクロのマッチングを実装しよう - 5. match
下準備が長かったけどいよいよ On Lispの match を実装する。
match は引数 x, y を比較しマッチしているか確認する。
- x と y が等しい場合はマッチする
- _ は何とでもマッチする(何もないとはマッチしない)
- x や y が既に束縛されているなら値を展開してから再度 match を呼ぶ
- x と y が pair だったら car と cdr の順に match を呼び出す。(結果的にリストの要素を比較していく)
(define match (lambda (x y . binds) (let ((bn (if (pair? binds) (car binds) binds))) (acond2 ((or (eqv? x y) (eqv? x '_) (eqv? y '_)) (values bn #t)) ((binding x bn) (match it y bn)) ((binding y bn) (match x it bn)) ((varsym? x) (values (cons (cons x y) bn) #t)) ((varsym? y) (values (cons (cons y x) bn) #t)) ((and (pair? x) (pair? y) (match (car x) (car y) bn)) (match (cdr x) (cdr y) it)) (#t (values #f #f))))))
テストコード
match のテスト。
どのように match するか分かると思う。
(assert-check-equal equal? ('((?x . 3)) (match '?x 3)) ('((?y . 3) (?x . 2)) (match '(?x . ?y) '(2 . 3))) ('((?y . 3) (?x . 2)) (match '(?x ?y _) '(2 3 4))) ('((?z . (hoge (c))) (?y . (display b)) (?x . (pair? a))) (match '(if ?x ?y ?z) '(if (pair? a) (display b) (hoge (c))))) ('((?z . (display a)) (?y . 3) (?x . a)) (match '(let ((?x ?y)) (_ ?z)) '(let ((a 3)) (begin (display a))))) ('((?z . (display a)) (?y . 3) (?x . a)) (match '(let ((?x ?y)) (_ ?z)) '(let ((a 3)) (print (display a))))) )