EoPL reading (113) 2.3 Representation Strategies for Data Types

Exercise 2.23

single pair of ribs で実装ときた。これって

gosh> (empty-env)
()
gosh>

で、

gosh> (extend-env '(y) '(8) (empty-env))
((y) . (8))
gosh>

で、

gosh> (extend-env '(d x) '(6 7) (exptend-env '(y) '(8) (empty-env)))
((d x y) . (6 7 8))
gosh>

という事なのかなぁ。
とりあえず上記な試験はさくっと書けそう。当たり前ですが、問題は実装ですね。extend-env はこう?

(define extend-env
  (lambda (syms vals env)
    (cons (append syms (car env)) (append vals (cdr env)))))

どっちかというと apply-env が問題。てーか上記な実装だと検索が繰り返しでなくなるんですが、extend する前の環境に復帰できませんがそれは OK なのだろーか。
とりあえず env 戻せるようにするには env が手続き戻す方式じゃないと微妙か。

(define extend-env
  (lambda (syms vals env)
    (lambda (sym)
      (let ((pos (list-find-position sym syms)))
        (if (number? pos)
            (list-ref vals pos)
            (apply-env env sym))))))

む。これって例示されてるそのまんまだな。env を脱ぎたい場合はこれで、そうでなければ上記で良いのでしょうか。
とりあえず以下ってコトで。

(define empty-env
  (lambda () (cons '() '())))

(define extend-env
  (lambda (syms vals env)
    (cons (append syms (car env)) (append vals (cdr env)))))

(define apply-env
  (lambda (env sym)
    (if (null? env)
	(eopl:error 'apply-env
		    "No binding for " sym)
	(let ((syms (car env))
	      (vals (cdr env)))
	  (let ((pos (list-find-position sym syms)))
	    (if (number? pos)
		(list-ref vals pos)
		(eopl:error 'apply-env
			    "No bindicatng for " sym)))))))

(define list-find-position
  (lambda (sym los)
    (list-index (lambda (sym1) (eqv? sym1 sym)) los)))

(define list-index
  (lambda (pred ls)
    (cond ((null? ls) #f)
          ((pred (car ls)) 0)
          (else
           (let ((list-index-r (list-index pred (cdr ls))))
             (if (number? list-index-r)
                 (+ list-index-r 1)
                 #f))))))

とり急ぎ作った試験が以下。

(use gauche.test)

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

(test-start "extend-env")
(test-section "empty-env")
(test* "empty env is '(cons '() '())"
       '(())
       (empty-env))

(test-section "extend-env")
(test* "(extend-env '() '() (empty-env))"
       '(())
       (extend-env '() '() (empty-env)))
(test* "(extend-env '(a) '(1) (empty-env))"
       '((a) 1)
       (extend-env '(a) '(1) (empty-env)))
(test* "(extend-env '(d x) '(6 7)
          (extend-env '(y) '(8)
            (empty-env)))"
       '((d x y) 6 7 8)
       (extend-env '(d x) '(6 7)
		   (extend-env '(y) '(8)
			       (empty-env))))
	      
(test-section "abnormal end")
(test* "apply empty-env"
       *test-error*
       (apply-env (empty-env) 'a))
(test* "search 'a from '(b c)"
       *test-error*
       (apply-env (extend-env '(b c) '(0 1) (empty-env))
		  'a))
(test* "search 'z from '((a) (b c))"
       *test-error*
       (apply-env (extend-env '(a) '(0)
			      (extend-env '(b c) '(1 2) (empty-env)))
		  'z))
       
(test-section "normal end")
(test* "search 'a from '(a b)"
       0
       (apply-env (extend-env '(a b) '(0 1) (empty-env))
		  'a))
(test* "search 'b from '(a b)"
       1
       (apply-env (extend-env '(a b) '(0 1) (empty-env))
		  'b))
(test* "search 'c from '((a b) (c))"
       2
       (apply-env (extend-env '(a b) '(0 1)
			      (extend-env '(c) '(2) (empty-env)))
		  'c))

(test-end)

試験にはパスしてるんですが、extend-env が戻す値について微妙な試行錯誤が御座いました。英語の練習してから着手可能であれば次に着手してみる方向で。

そーゆー意味ではいっちゃん上の戻り予想は完全ダウトですな。

gosh> (empty-env)
(())
gosh>

で、

gosh> (extend-env '(y) '(8) (empty-env))
((y) 8)
gosh>

で、

gosh> (extend-env '(d x) '(6 7) (exptend-env '(y) '(8) (empty-env)))
((d x y) 6 7 8)
gosh>

なのか。とほほ。