SICP 読み (142) 4.1.2 式の表現

久々の酷い頭痛。某ネットワーク機器メーカさんは保守契約しないと設置サポートしねぇ、っていいやがるし今日はどうにも日が悪い。(何
で、現実トウヒ分も含め、ぼさっと検討を始めたんですがなかなかに興味深いのが問題 4.5 ッス。

訂正

なんかもの凄い大ボケをカマシてる事が判明。以下、修正済みです。

問題 4.5

p.221 にて例示されている cond 関連の一連の手続きが以下。

(define (last-exp? seq) (null? (cdr seq)))
(define (first-exp seq) (car seq))

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

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

(define (cond->if exp)
  (expand-clauses (cond-clauses exp)))

(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)
                     (sequence->exp (cond-actions first))
                     (expand-clauses rest))))))

手を入れる必要があるのは expand-clauses のはず。else な処理は手を入れなくても良いかな。
# ケツの else って何だろ。(訂正済み)自分で書いてて意味不明。
てーか、以下がどんな形に変換されないとイケないのか。

(cond ((assoc 'b '((a 1) (b 2))) => cadr)
      (else false))

例示されている上記の例はどうなれば良いのか。とりあえず let が使えれば良いのですが

(let ((x (assoc 'b '((a 1) (b 2)))))
  (if x
      (cadr x)
      false))

みたいな感じで。でも cond を if に書き換える、という方式だと let 使って置き換えようとすると、let の中で定義する変数名をどうすりゃ良いのか、という話になる。
でも、以下なソレだと

(if (assoc 'b '((a 1) (b 2)))
    (cadr (assoc 'b '((a 1) (b 2))))
    false)

ちょっと頭悪いなぁ、というか predicate なソレが重い手続きだと駄目じゃね? と。
で、何かわしの知らんヒミツの手続きというか特殊形式があるのか、と R5RS 見てみると 7.3 節に define-syntax 使った cond の定義の例が出ていて、当たり前なんですが let が使ってある。
うーん。define-syntax を含め、scheme の実装ってどうなってるんだろう。気になる。

とりあえず、let を使わない方式なソレが以下です。

(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)
		     (if (equal? '=> (cadr first))
			 (list (caddr first) (cond-predicate first))
			 (sequence->exp (cond-actions first)))
		     (expand-clauses rest))))))

うーん。これって試験できるんだったっけ? あ、あるある。まず上記の修正を盛り込んで既存の試験にはパス。既存なソレに試験を追加したナニを以下に。

   ("expand-clauses"
    (assert-equal 'false (expand-clauses '()))
    (assert-error (lambda () (expand-clauses '((else (x)) ((= x 1) (y))))))
    (let ((l '(cond ((last-exp? exps) (eval (first-exp exps) env))
		    (else (eval (first-exp exps) env)
			  (eval-sequence (rest-exps exps) env)))))
      (let ((if-S '(if (last-exp? exps)
		       (eval (first-exp exps) env)
		       (begin (eval (first-exp exps) env)
			      (eval-sequence (rest-exps exps) env)))))
	(assert-equal if-S (expand-clauses (cond-clauses l)))))
    (let ((l '(cond ((null? seq) seq)
		    ((last-exp? seq) (first-exp seq))
		    (else (make-begin seq)))))
      (let ((if-S '(if (null? seq)
		       seq
		       (if (last-exp? seq)
			   (first-exp seq)
			   (make-begin seq)))))
	(assert-equal if-S (expand-clauses (cond-clauses l)))))
    (let ((l '(cond ((assoc 'b '((a 1) (b 2))) => cadr)
		    (else false))))
      (let ((if-S '(if (assoc 'b '((a 1) (b 2)))
		       (cadr (assoc 'b '((a 1) (b 2))))
		       false)))
	(assert-equal if-S (expand-clauses (cond-clauses l)))))
    )

一応試験にはパスしてます。引き続き、もすこしスマートな解を検討してみます。

追記

投入したエントリが無茶苦茶だった。恥ずかしひ。