プログラミング再入門

プログラミングをもう一度ちゃんと勉強する読書ノート

SICP 4.1.4 Running the Evaluator as a Program

漸く核心の手続き評価部分。

ノート

評価器はプログラムを最終的にプリミティブによるプログラムまで簡約するので、最終的にはプリミティブをベースのLisp上で実行する事になる。

最初の環境を用意する。そこには言語として最初から定義されている値やプリミティブを用意しておく。

ここではプリミティブをprimitiveと言うシンボルと、ベースのLispの手続きのリストで表す事にする。
脚注17の

(define apply-in-underlying-scheme apply)

はRacketモードでは上手く行かなかったので、テキストのapplyをapply-procと名前を変更する。
これでプリミティブが動く様になった。

> (eval '(define a 1) the-global-environment)
'ok
> (eval '(define b 2) the-global-environment)
'ok
> (eval '(cons a b) the-global-environment)
'(1 . 2)
> 

REPLも作る。
テキストにはthe-global-environmentを定義する所から書いてあるが、Racketモードではdriver-loopが既にthe-global-environementを参照しているので、この評価器を走らせる前にthe-global-environmentは定義されていなければならなかった。テキストの例を動かしてみる。

> (driver-loop)
;;; M-Eval input:
(define (append x y)
  (if (null? x)
      y
      (cons (car x)
            (append (cdr x) y))))
;;; M-Eval value:
ok
;;; M-Eval input:
(append '(a b c) '(d e f))
;;; M-Eval value:
(a b c d e f)
;;; M-Eval input:

DrRacketでは入力する場面では四角い枠が出て来るのでM-Eval input:のプロンプトは意味が無かった。

Exercise 4.14

nullをinitial-envに定義して、listをプリミティブに追加して簡単なmapを自前定義してみる。

> (driver-loop)
;;; M-Eval input:
(define (map f lst) (if (null? lst) null (cons (f (car lst)) (map f (cdr lst)))))
;;; M-Eval value:
ok
;;; M-Eval input:
(map car (list (list 1 2) (list 3 4)))
;;; M-Eval value:
(1 3)
;;; M-Eval input:

一応動いている。
mapをプリミティブに追加してみると。

> (driver-loop)
;;; M-Eval input:
(map car (list (list 1 2) (list 3 4)))
. . map: contract violation
  expected: procedure?
  given: '(primitive #<procedure:car>)
  argument position: 1st
  other arguments...:
   '((1 2) (3 4))
> 

プリミティブのmapは関数としてベースのLispの関数を期待してしまうが、引数のcarは評価しているLisp言語のcarを参照していて、これはベースのLispにとってはただのデータのリストなのでエラーとなってしまう。