SICP 読み (160) 4.1.3 評価器のデータ構造

現実トウヒの積み重ねだったりする。(何

問題 4.11

この問題の直前の時点では例えば setup-environment が以下だった場合

(define (setup-environment)
  (let ((initial-env
	 (extend-environment '(x y)
			     '(1 2)
			     the-empty-environment)))
    initial-env))

には

(((x y) 1 2))

なリストになっている。
問題の意図としてはこれが

(((x 1) y 2))

になってれば OK ってコトなのかなぁ。違うな。以下??

gosh> (cons (cons (cons 'x 1) (cons 'y 2)) '())
(((x . 1) y . 2))
gosh>

追加検討

む。駄目だな。car と cdr で variable と value を切り分けようとしていたから

(((x y) 1 2))

で良かったのではないか、と。取り扱うにあたって

(((x . 1) (y . 2)))

になってた方が取り出しやすい。

その後

並びの順が変わる事で影響がありそげなのは

  • apply
  • extend-environment
  • make-frame
  • frame-variables
  • frame-values
  • add-binding-to-frame!
  • lookup-variable-value
  • set-variable-value!
  • define-variable!

上手に直せば上記のいくつかは修正不要と見た。って frame マワリだけで良いかも??

検討

これでは並び順が逆になるんだよねぇ。

(define (make-frame variables values)
  (let f ((result '()) (variables variables) (values values))
    (cond ((null? variables) result)
	  (else
	   (f (cons (cons (car variables) (car values)) result)
	      (cdr variables)
	      (cdr values))))))

これはこれで微妙??

(define (make-frame variables values)
  (let f ((result '()) (variables variables) (values values))
    (cond ((null? variables) result)
	  (else
	   (f (append result (list (cons (car variables) (car values))))
	      (cdr variables)
	      (cdr values))))))

あら、できたみたい。なんか微妙。最近このパターンってどこかで出てきたようなオボロげな記憶があるんですが忘却の彼方。
こうなってれば frame-variable も frame-value も map 使ってなんとかなる。あと、add-binding-to-frame! も修正が必要。こんな感じか。

(define (frame-variables frame) (map car frame))
(define (frame-values frame) (map cdr frame))
(define (add-binding-to-frame! var val frame)
  (append (list (cons var val)) frame))

はたしてこれで lookup だの set だの define だのが動くのか。(を

追記

add-binding-to-frame! は上記では駄目だな。今んトコ set! なやり方しか思いつかん。

(define (add-binding-to-frame! var val frame)
  (set! frame (append (list (cons var val)) frame)))

と思ったらこれでも駄目。

gosh> (define f (make-frame '(x y z) '(1 2 3)))
f
gosh> f
((x . 1) (y . 2) (z . 3))
gosh> (add-binding-to-frame! 'o 0 f)
((o . 0) (x . 1) (y . 2) (z . 3))
gosh> f
((x . 1) (y . 2) (z . 3))
gosh>

とほほ。末端にくっつけるのは手間だけど楽そげ。先頭に、なソレはどうやれば良いのだろうか。末端くっつけ案が以下。

(define (add-binding-to-frame! var val frame)
  (let f ((var var) (val val) (frame frame))
    (cond ((null? (cdr frame))
	   (set-cdr! frame (cons (cons var val) '())))
	  (else
	   (f var val (cdr frame))))))

もう少し考えてみたいけど、全体に盛り込んで試験した方が前向きかも。

盛り込み

とりあえず盛り込んで既存の試験を実行してみたんですが NG。frame な試験があるようなので、そちら方面は今から修正。
と、軽微なソレ達の修正をしていたんですが、微妙なエラー有り。試験が以下。

   ("define & assignment"
    (let ((new-env (extend-environment '(x) '(1) the-global-environment)))
      (eval '(define y 2) new-env)
      (assert-equal 1 (eval 'x new-env))
      (assert-equal 2 (eval 'y new-env))
      (eval '(set! y 3) new-env)
      (assert-equal 3 (eval 'y new-env))
      )
    )

最後の assert がダメ。y を define して値の確認までは OK な模様ですが、set! した後の値の確認で NG。うーん ... frame マワリのみとゆーのは見込み違いな模様。

アマかった。続きがあるかどうか微妙。

続き

とりあえず lookup はこのままで動くと思われるんですが、データ構造が変わっているので根本的に考えなおす必要あり。なんで frame の変更だけで大丈夫と思ったんだろうか。いつもの事ながら浅はかスギるなぁ。
最初のソレより、こっちの方がやり易い印象があるんですが、さくっとイメージできん。例えば lookup だとこんな感じ??

(define (lookup-variable-value var env)
  (define (env-loop env)
    (define (scan f)
      (cond ((null? f)
	     (env-loop (enclosing-environment env)))
	    ((eq? var (car (car f)))
	     (cdr (car f)))
	    (else (scan (cdr f)))))
    (if (eq? env the-empty-environment)
	(error "Unbound variable" var)
	(let ((frame (first-frame env)))
	  (scan frame))))
  (env-loop env))

なんとなくイケてるっぽい感じ。同じ要領で他もいければ良いんですが。とゆー事で set が以下。

(define (set-variable-value! var val env)
  (define (env-loop env)
    (define (scan f)
      (cond ((null? f)
	     (env-loop (enclosing-environment env)))
	    ((eq? var (car (car f)))
	     (set-cdr! (car f) val))
	    (else (scan (cdr f)))))
    (if (eq? env the-empty-environment)
	(error "Unbound variable -- SET!" var)
	(let ((frame (first-frame env)))
	  (scan frame))))
  (env-loop env))

これもイケてる感じです。define が残っている。

(define (define-variable! var val env)
  (let ((frame (first-frame env)))
    (define (scan f)
      (cond ((null? f)
	     (add-binding-to-frame! var val frame))
	    ((eq? var (car (car f)))
	     (set-cdr! (car f) val))
	    (else (scan (cdr f)))))
    (scan frame)))

ちょっと既存のソレとは別に試験を。本来なら既存のソレで満足な試験がナニなはずなんですが駄目だな。以下に追加分の試験を。

  ("4.11"
   ("lookup"
    (let ((nenv (extend-environment '(x) '(1) the-global-environment)))
      (assert-equal 1 (lookup-variable-value 'x nenv))
      (assert-error (lambda () (lookup-variable-value 'y nenv)))
      (assert-true (lookup-variable-value 'true nenv))
      )
    )

   ("set"
    (let ((nenv (extend-environment '(x) '(1) the-global-environment)))
      (eval '(set! x 2) nenv)
      (assert-equal 2 (lookup-variable-value 'x nenv))
      (assert-error (lambda () (eval '(set y 2) nenv)))
      )
    )

   ("define"
    (let ((nenv (extend-environment '(x) '(1) the-global-environment)))
      (eval '(define y 3) nenv)
      (assert-equal 3 (lookup-variable-value 'y nenv))
      )
    )
   )

既存なソレで微妙に動かないのがいくつか。どこを直した、と列挙し始めるとキリが無いので略。(を