問題4.5

(define (cond->if exp)
  (expand-clauses (cond-clauses exp)))
(define (make-if predicate consequent alternative)
  (list 'if predicate consequent alternative))

(define (cond-clauses exp) (cdr exp))
(define (cond-predicate clause) (car clause))
(define (cond-actions clause) (cdr clause))
(define (cond-else-clause? clause) (eq? (cond-predicate clause) 'else))

(define (first-exp seq) (car seq))
(define (rest-exps seq) (cdr seq))
(define (last-exp? seq) (null? (rest-exps seq)))
(define (make-begin seq (cons 'begin seq)))
(define (sequence->exp seq)
  (cond ((null? seq) seq)
        ((last-exp? seq) (first-exp seq))
        (else (make-begin seq))))

(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))

            (make-if (cond-predicate first)
                     (let ((action (cond-actions first))
                           (predicate (cond-predicate first)))
                       (if (eq? (car action) '=>)
                           (list (cadr action) predicate)
                           (sequence->exp action)))

                     (expand-clauses rest))))))

;; と書きたいところだが、これだと、 (cond-predicate first) がダブっているため、predicate文に副作用のあるコードだとわかりにくいバグを仕込むことになる。

;; たとえば、
(cond->if '(cond ((x #f) => (lambda (y) (print "(x #f)='" y "' is true !.")))
                 (else '())))

;=> (if (x #f) ((lambda (y) (print "(x #f)='" y "' is true !.")) (x #f)) '())

(define x
  (let ((z #t))
    (lambda (y)
      (let ((buf z))
        (set! z y)
        buf))))

(eval '(if (x #f) ((lambda (y) (print "(x #f)='" y "' is true !.")) (x #f)) '()) '())
;=> (x #f)='#f' is true !.


;; よってこうやる。
(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))
            (if (eq? (cadr first) '=>)

                (let ((sym (gensym)))
                  (list 'let (list (list sym (cond-predicate first)))
                        (list 'if
                              sym
                              (list (caddr first) sym)
                              (expand-clauses rest))))

                (make-if (cond-predicate first)
                         (sequence->exp action)
                         (expand-clauses rest)))))))


;; これで
(cond->if '(cond ((x #f) => (lambda (y) (print "(x #f)='" y "' is true !.")))
                 (else '())))

;=> (let ((G1 (x #f))) (if G1 ((lambda (y) (print "(x #f)='" y "' is true !.")) G1) '()))

(define x
  (let ((z #t))
    (lambda (y)
      (let ((buf z))
        (set! z y)
        buf))))

(eval '(let ((G1 (x #f))) (if G1 ((lambda (y) (print "(x #f)='" y "' is true !.")) G1) '())) '())
;(x #f)='#t' is true !.

;; ok !
;;どちらにせよ (gensym) は必要。これが正解!