問題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式の中に( => )のような条件分岐があっても対応できるようにします。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式が変換されたことになるのではと考えられます。