(sicp50)m4.9のdoの修正と4.10

4.10 好きな制御構造の作成

dotimesを作ってみた。(何か大げさな様な気もするけど)

;;使用例
 (let ((fact 1))
   (dotimes (e 10 fact) (set! fact (* fact (+ 1 e)))))
;; gosh> 3628800

named-letを使った再帰に置き換えて定義する。

(define (run-dotimes exp)
  (let ((definition (cadr exp))
        (body (caddr exp)))
    (let ((var (car definition))
          (last (cadr definition))
          (ret-value (if (null? (cddr definition))
                         '() ;defaultの戻り値
                         (caddr definition))))
      (make-let (list 'loop (list (list var 0))
                      (make-if (list '>= var last)
                               ret-value
                               (make-begin (list body
                                                (list 'loop (list '+ var 1))))))))))
  (print  (run-dotimes '(dotimes (n 3 'done) (print n))))
;; gosh> (let loop ((n 0)) (if (>= n 3) 'done (begin (print n) (loop (+ n 1)))))

evalに組み合わせる

(define (eval-dotimes exp env)
  (let ((first (car exp))
        (rest (cdr exp)))
    (eval-sequence (list (eval-dotimes first)
                         rest)
                   env)))

doを使ってdotimesを定義する方が自然かもしれない。

問題4.9の修正

doの定義も勘違いしていた。(bodyを受け取っていない。)

(define (run-do exp)
  (let ((binding (cadr exp))
        (predicate&value (caddr exp))
        (body (if (null? (cdddr exp)) '() (cadddr exp))))
    (let ((vars&exps (map (lambda (e) (list (car e) (cadr e))) binding))
          (set-f (map caddr binding)))
      (make-let (list 'loop vars&exps
                      (make-if (car predicate&value)
                               (cadr predicate&value)
                               (if body
                                   (make-begin (list body (cons 'loop set-f)))
                                   (cons 'loop set-f))))))))


(run-do '(do ((n 0 (+ n (car l))) (l l (cdr l))) ((null? l) n) (print n)))
;; gosh> (let loop ((n 0) (l l)) (if (null? l) n (begin (print n) (loop (+ n (car l)) (cdr l)))))

doを使ったdotimesの定義

(define (make-do binding predicate&ret-v body) 
  (list 'do binding predicate&ret-v body))

(define (run-dotimes2 exp)
  (let ((definition (cadr exp))
        (body (caddr exp)))
    (let ((var (car definition))
          (limit (cadr definition))
          (ret-value (if (null? (cddr definition))
                         '() ;defaultの戻り値
                         (caddr definition))))
      (make-do (list (list 'tlimit limit) (list var 0 (list '+ var 1)))
               (list (list '>=  var 'tlimit) ret-value)
               body))))

(run-dotimes2 '(dotimes (n 3 'done) (print n)))
;; gosh> (do ((tlimit 3) (n 0 (+ n 1))) ((>= n tlimit) 'done) (print n))

実行可能な式ができればいいのかな?
何か勘違いしているところがあるかもしれない。