SICP 4.1.7-4.2.1

4.22

問題4.6では、 let->combinationを実装することで実現している。 ここでも同じアプローチを使う。

let->combination は 4.6 で定義済みなので、 analyze手続きに 以下を追記する

(define (analyze exp)
  (cond ((self-evaluating? exp)
         (analyze-self-evaluating exp))
         ;;(中略)
        ((let? exp) (analyze (let->combination exp))) ;; 追記
        ((application? exp) (analyze-application exp))
        (else
         (error "Unknown expression type -- ANALYZE" exp))))

4.23

本文の定義

(define (analyze-sequence exps)
  (define (sequentially proc1 proc2)
    (lambda (env) (proc1 env) (proc2 env)))
  (define (loop first-proc rest-procs)
    (if (null? rest-procs)
        first-proc
        (loop (sequentially first-proc (car rest-procs))
              (cdr rest-procs))))
  (let ((procs (map analyze exps)))
    (if (null? procs)
        (error "Empty sequence -- ANALYZE"))
    (loop (car procs) (cdr procs))))

Alyssaの定義

(define (analyze-sequence exps)
  (define (execute-sequence procs env)
    (cond ((null? (cdr procs)) ((car procs) env))
          (else ((car procs) env)
                (execute-sequence (cdr procs) env))))
  (let ((procs (map analyze exps)))
    (if (null? procs)
        (error "Empty sequence -- ANALYZE"))
    (lambda (env) (execute-sequence procs env))))

並びが一つの場合

本文の実装

loop に評価される rest-procs がnilになる。 そのため、 first-procそのものが返ってくる。

alyssaの実装

lambda式 (lambda (env) (execute-sequence procs env)) が返ってくる

並びが2つの場合

本文の実装

説明しにくいが、 loopによって 順次 sequectially 手続きが適用された lambda 式が返ってくる 要は 各式に対して (loop (sequentially first-proc (car rest-procs)) (cdr rest-procs)))) によって、 (lambda (env) (proc1 env) (proc2 env))) が順次適用された結果が返ってくる。

alyssaの実装

1つの場合と同じなので、 式のリスト(procs)が含まれた lambda式 (lambda (env) (execute-sequence procs env)) が返ってくる

2つの比較

まとめると、 本文の実装は

(begin
 <procA>
 <procB>
 <procC>)

のような入力に対して、

(lambda (env)
  (lambda (env)
    (lambda (env) (<eval-procA> env))
    (<eval-procB> env))
  (<eval-procC> env))

という手続きが作られる。

Alyssaの場合は

(labmda (env)
  (execute-sequence
    (list
      (lambda (env) (<eval-procA> env))
      (lambda (env) (<eval-procB> env))
      (lambda (env) (<eval-procC> env)))))

という手続きになる。 この結果、個々のシーケンスの解釈が実行時に都度行われてしまう。

つまり本文にある

lambda式の解析は, 効率に大きな利益をもたらす: lambdaの評価の結果の手続きが多数回作用されようとも, lambda本体は一回だけ解析される.

この恩恵が失われてしまうことになる。

4.24

試してないが、未分離のモノより分離したモノの方が、構文解析の処理が減る分早くなるはず。

4.25

停止しない。

作用的順序では n=1の場合にも (factorial (- n 1)) を評価してしまうため、再帰が終わらない。

正規順序の場合、 n=1 のときには (factorial (- n 1)) が評価されないので、正常動作する。

4.26

unless->if を作って実装する

; analyze に ((unless? exp) (analyze (unless->if exp))) を追加

(define (unless? exp) (tagged-list? exp 'unless))

;;(unless condition usual-value exceptional-value)
(define (unless-condition exp) (cadr exp))
(define (unless-usual-value exp) (caddr exp))
(define (unless-exceptional-value exp)
  (if (null? (cdddr exp))
      #f ;; exceptional-valueを省略した場合
      (cadddr exp)))

;;(unless condition usual-value exceptional-value)
;;  ->  (if condition exceptional-value usual-value)
(define (unless->if exp)
  (make-if (unless-condition exp)
           (unless-exceptional-value exp)
           (unless-usual-value exp)))