問題4-4〜6
問題4-4。andとorの式を扱えるようにします。eval手続き内で、
(define (eval exp env) (cond (..) ;;others ((and? exp) (eval-and (and-predicates exp) env)) ((or? exp) (eval-or (or-predicates exp) env)) ;;others ))
のような型判別と評価を行なうことを目指します。まずは、型判定のロジック。
(define (and? exp) (tagged-list? exp 'and)) (define (or? exp) (tagged-list? exp 'or))
次に、eval-andを実装します。andの先頭要素がfalseなら、すぐさまfalseを返すようにして、trueならandの次の要素を調べていきます。
(define (eval-and seq env) (if ((last-and? seq) (eval (first-and seq) env)) (if (false? (eval (first-and seq) env)) 'false (eval-and (rest-and seq) env)))) (define (and-predicates exp) (cdr exp)) (define (last-and? seq) (null? (cdr seq))) (define (first-and seq) (car seq)) (define (rest-and seq) (cdr seq))
同様に、eval-orも実装します。orの先頭要素がtrueなら、すぐさまtrueを返すようにして、falseならorの次の要素を調べていきます。
(define (eval-or seq env) (if ((last-or? seq) (eval (first-or seq) env)) (if (true? (eval (first-or seq) env)) 'true (eval-or (rest-or seq) env)))) (define (or-predicates exp) (cdr exp)) (define (last-or? seq) (null? (cdr seq))) (define (first-or seq) (car seq)) (define (rest-or seq) (cdr seq))
問題4-5。cond式の中に(
(define (cond-extended? clause) (eq? (cadr clause) '=>))
拡張版を利用する時は、recipientに位置する要素が、testを引数とする手続きになります。そのrecipientを以下の手続きによって取得します。
(define (cond-extended-proc clause) (caddr clause))
この二つの手続きをexpand-clausesに組み込みます。condのelse節以外の場合は、make-ifを使ってif文を構築しますが、if文が真だった時の処理を変更してやります。その節が=>バージョンだったら、recipientをtestに作用させます。
(define (expand-clauses clauses) (if (null? clauses) 'false (let ((first (car clauses)) (rest (cdr clauses))) (if (cond-else-clause? first) (if (null? rest) (sequence->exp (cond-actions first)) (error "ELSE clause isn't last -- COND->IF" clauses)) (let ((predicate (cond-predicate first))) (make-if predicate (if (cond-extended? first) ;; 拡張版か否かの判定 ((cond-extended-proc first) predicate) ;;拡張版のロジック (sequence->exp (cond-actions first))) ;;通常版のロジック (expand-clauses rest)))))))
問題4-6。let式を扱えるようにします。まず、evalの中には、以下のような条件分岐を追加します。
(define (eval exp env) (cond (..) ;;others ((let? exp) (eval (let-combination exp) env)) ;;others ))
let式かどうかは、以下のように判別します。また、let式の定義部分や本体部分を取得する手続きも定義します。
(define (let? exp) (tagged-list? exp 'let)) (define (let-definition exp) (cadr exp)) (define (let-body exp) (caddr exp))
let->combinationは、以下のように定義します。
(define (let->combination exp) (cons (make-lambda (make-let->lambda-vars (let-definition exp)) (let-body exp)) (make-let->lambda-exps (let-definition exp))))
make-let->lambda-varsにlet式の定義部分を渡すことで、lambdaの仮パラメータのリストが返ってきます。
(define (make-let->lambda-vars let-definition) (if (null? let-definition) '() (cons (caar let-definition) (make-let->lambda-vars (cdr let-definition)))))
同様に、make-let->lambda-expsにlet式の定義部分を渡すことで、lambdaのパラメータのリストが返ってきます。
(define (make-let-lambda-exps let-definition) (if (null? let-definition) '() (cons (cdar let-definition) (make-let->lambda-exps (cdr let-definition)))))
let->combinationの中で、make-lambdaを使ってlambda式を作りつつ、それに引数のリストを結合することで、let式が変換されたことになるのではと考えられます。