SICP問題4.8

名前付きlet

(let <var> <bindings> <body>)

名前付きletで定義したfib手続き

(define (fib n)
  (let fib-iter ((a 1)
                 (b 0)
                 (count n))
    (if (= count 0)
        b
        (fib-iter (+ a b) a (- count 1)))))

これは以下と同じである

(define (fib n)
  (define (fib-iter a b count)
    (if (= count 0)
        b
        (fib-iter (+ a b) a (- count 1))))
  (fib-iter 1 0 n))

と思ったんだけど、これだと二つのリスト返さないといけないのでbeginで包み込まないとダメなので以下のように書き換えを行う

(define (fib n)
  (begin
    (define (fib-iter a b count)
      (if (= count 0)
          b
          (fib-iter (+ a b) a (- count 1))))
    (fib-iter 1 0 n)))

今回の書き換えとは関係ないけど、以下とも同じ

(define (fib n)
  (letrec ((fib-iter
            (lambda (a b count)
              (if (= count 0)
                  b
                (fib-iter (+ a b) a (- count 1))))))
          (fib-iter 1 0 n)))

ということで、上の書き換えからlet関連の手続きを修正する

(define (let? exp)
  (tagged-list? exp 'let))
(define (named-let? exp)                ; 追加
  (and (let? exp)
       (not (pair? (cadr exp)))))
(define (let-parameters exp)            ; 修正
  (if (named-let? exp)
      (caddr exp)
      (cadr exp)))
(define (let-body exp)                  ; 修正
  (if (named-let? exp)
      (cdddr exp)
      (cddr exp)))
(define (let-variables exp)
  (map car (let-parameters exp)))
(define (let-expressions exp)
  (map cadr (let-parameters exp)))
(define (let-name exp)                  ; 追加
  (cadr exp))
(define (make-definition variable body) ; 追加
  (list 'define variable body))
(define (let->combination exp)          ; 修正
  (if (named-let? exp)
      (if (null? (let-parameters exp))
          '()
          (make-begin
           (list
            (make-definition (let-name exp)
                             (make-lambda (let-variables exp) (let-body exp)))
            (cons (let-name exp) (let-expressions exp)))))
      (if (null? (let-parameters exp))
          '()
          (cons
           (make-lambda (let-variables exp) (let-body exp))
           (let-expressions exp)))))