問題4.4-4.6
続きです。
4.4
orとandを導入します。eval本体には次のように入れます。
... ((and? exp) (eval-and (and-body exp) env)) ((or? exp) (eval-or (or-body exp) env)) ...
andとorの抽象化は次のようにします。
(define (and? exp) (tagged-list? exp 'and)) (define (and-body exp) (cdr exp)) (define (first-exp seq) (car seq)) (define (rest-exps seq) (cdr seq)) (define (last-exp? seq) (null? (cdr seq))) (define (eval-and seq env) (let ((exp-val (eval (first-exp seq) env))) (cond ((eq? false exp-val) false) ((last-exp? seq) exp-val) (else (eval-and (rest-exps seq) env))))) (define (or? exp) (tagged-list? exp 'or)) (define (first-exp seq) (car seq)) (define (rest-exps seq) (cdr seq)) (define (last-exp? seq) (null? (cdr seq))) (define (or-body exp) (cdr exp)) (define (eval-or seq env) (let ((exp-val (eval (first-exp seq) env))) (cond ((eq? true exp-val) true) ((last-exp? seq) exp-val) (else (eval-or (rest-exps seq) env)))))
例のごとく動かせないので、ほんとに合ってるかどうかは分かりません。
次に導出された式として、andとorを実装します。まずand。
(define (and? exp) (tagged-list? exp 'and)) (define (and-body exp) (cdr exp)) (define (first-exp seq) (car seq)) (define (rest-exps seq) (cdr seq)) (define (last-exp? seq) (null? (cdr seq))) (define (and->if exp) (and-expand-clauses (and-body exp))) (define (and-expand-clauses clauses) (if (null? clauses) 'true (let ((first (first-exp clauses)) (rest (rest-exps clauses))) (if (last-exp? clauses) first (make-if (list 'eq? 'false first) 'false (and-expand-clauses rest)))))) (define (eval-and exp env) (eval (and->if exp) env))
次にor。orはandの名前をandとor、falseとtrueを反転させるだけでいい。
(define (or? exp) (tagged-list? exp 'or)) (define (or-body exp) (cdr exp)) (define (first-exp seq) (car seq)) (define (rest-exps seq) (cdr seq)) (define (last-exp? seq) (null? (cdr seq))) (define (or->if exp) (or-expand-clauses (or-body exp))) (define (or-expand-clauses clauses) (if (null? clauses) 'false (let ((first (first-exp clauses)) (rest (rest-exps clauses))) (if (last-exp? clauses) first (make-if (list 'eq? 'true first) 'true (or-expand-clauses rest)))))) (define (eval-or exp env) (eval (or->if exp) env))
4.5
動かせないとつまらないので、動けるところまで実装しました。やっぱり動かすと楽しい。
condのもう一つの構文を実装するらしいです。こんな構文あるの知らなかった。
まず、こういうのを追加して、
(define (cond-normal-clause? clause) (= 2 (length clause))) (define (cond-=>-character? clause) (eq? (cadr clause) '=>))
cond-actionsを次のように変更して、
(define (cond-actions clause) (if (cond-normal-clause? clause) (cdr clause) (cddr clause)))
expand-clausesを変更する。
(define (expand-clauses clauses) (if (null? clauses) 'false (let ((first (car clauses)) (rest (cdr clauses))) (if (cond-normal-clause? first) (if (cond-else-clause? first) (if (null? rest) (sequence->exp (cond-actions first)) (error "ELSE clause isn't last -- COND->IF" clauses)) (make-if (cond-predicate first) (sequence->exp (cond-actions first)) (expand-clauses rest))) (if (cond-=>-character? first) (if (cond-else-clause? first) (if (null? rest) (list (sequence->exp (cond-actions first)) true) (error "ELSE clause isn't last -- COND->IF" clauses)) (let ((predicate (cond-predicate first))) (make-if predicate (list (sequence->exp (cond-actions first)) predicate) (expand-clauses rest)))) (error "syntax error -- COND"))))))
すると、次のようにちゃんと評価してくれる。
;;; M-Eval input: (define x 3) ;;; M-Eval value: ok ;;; M-Eval input: (cond ((= x 4) 4) (x => (lambda (y) (* y y y))) (else 88)) ;;; M-Eval value: 27
これはどういう時に便利なのか、よく分からないです。そのうち使いたくなるような場面に遭遇するのかな。
4.6
letを実装します。(let ((var val) ...)
)は((lambda (var ...) ) val ...)の糖衣構文であるので、導出された式として表現することができます。(define (let? exp) (tagged-list? exp 'let)) (define (let-clauses exp) (cadr exp)) (define (let-body exp) (cddr exp)) (define (let-clause-var clause) (car clause)) (define (let-clause-exp clause) (cadr clause)) (define (let->combination exp) (define (let-exp-expand clauses) (if (null? clauses) '() (cons (let-clause-exp (car clauses)) (let-exp-expand (cdr clauses))))) (cons (make-lambda (map let-clause-var (let-clauses exp)) (let-body exp)) (let-exp-expand (let-clauses exp))))
let->combinationでlambdaに変換しています。eval本体に、
... ((let? exp) (eval (let->combination exp) env)) ...
というのを追加します。すると、
;;; M-Eval input: (let ((x 3) (y 4) (z 5)) (+ x y z)) ;;; M-Eval value: 12 ;;; M-Eval input: (let ((x 80) (y 12) (z 8)) (+ x y z)) ;;; M-Eval value: 100
ちゃんと評価してくれます。楽しい。
今日はここまで。