exercise 4.5~4.10

ex4.5

こんな機能もあったのか、condよ・・・。

(define (cond-recipient-clause? clause)                                       
  (eq? (cadr clause) '=>))                                                    
(define (cond-recipient clause)                                               
  (caddr clause))

(define (expand-clauses clauses)                                              
  (if (null? clauses)                                                         
      'false                          ; no else clause                        
      (let ((first (car clauses))                                             
            (rest (cdr clauses)))                                             
        (cond ((cond-else-clause? first)                                      
               (if (null? rest)                                               
                   (sequence->exp (cond-actions first))                       
                   (error "ELSE clause isn't last -- COND->IF"                
                          clauses)))       
              ((cond-recipient-clause? first)                                 
               (list                                                          
                (make-lambda                                                  
                 '(test)                                                      
                 (list                                                        
                  (make-if 'test                                              
                           (list (cond-recipient first) 'test)                
                           (expand-clauses rest)))) (cond-predicate first)))  
              (else (make-if (cond-predicate first)                           
                             (sequence->exp (cond-actions first))             
                             (expand-clauses rest)))))))                      
                                                                  

結果は

;;; M-Eval input:                                                             
(cond ((assoc 'b '((a 1) (b 2))) => cadr)                                     
      (else false))                                                           
                                                                              
;;; M-Eval value:                                                             
2  

ちなみにassocとcadrはめんどいのでprimitive-proceduresのリストに放り込んで置いた。

ex4.6+4.8

やっとletか。
めんどくさいので4.8もまとめてやっちゃう。

(put 'eval 'let (lambda (exp env) (eval (let->combination exp) env)))
(define (named-let? exp)
  (and (not (pair? (cadr exp))) (not (eq? (cadr exp) '() ))))
(define (named-let-bindings exp)
  (caddr exp))
(define (named-let-var exp)
  (cadr exp))
(define (named-let-body exp)
  (cdddr exp))
(define (let-bindings exp)
  (cadr exp))
(define (let-body exp)
  (cddr exp))
(define (first-binding seq)
  (car seq))
(define (rest-bindings seq)
  (cdr seq))
(define (vars-of-bindings bindings)
  (if (null? bindings)
      '()
      (cons (car (first-binding bindings))
            (vars-of-bindings (rest-bindings bindings)))))
(define (exps-of-bindings bindings)
  (if (null? bindings)
      '()
      (cons (cadr (first-binding bindings))
            (exps-of-bindings (rest-bindings bindings)))))

(define (let->combination exp)
  (if (named-let? exp)
      (let ((name (named-let-var exp))
            (bindings (named-let-bindings exp)))
       (sequence->exp (list
         (list 'define name (make-lambda
                             (vars-of-bindings bindings)
                             (named-let-body exp)))
         (cons name(exps-of-bindings bindings)))))
      (let ((bindings (let-bindings exp)))
        (cons (make-lambda (vars-of-bindings bindings)
                           (let-body exp))
              (exps-of-bindings bindings)))))

しかしこれでいいのか named let …
適当過ぎる匂いがする。

ex4.7
(put 'eval 'let* (lambda (exp env) (eval (let*->nested-lets exp) env)))
(define (make-let bindings body)
  (cons 'let (cons bindings body)))
(define (let*->nested-lets exp)
  (define (iter bindings)
    (make-let (list (first-binding bindings))
              (if (null? (rest-bindings bindings))
                  (let-body exp)
                  (list (iter (rest-bindings bindings))))))
  (iter (let-bindings exp)))

(define (let*->combination exp)
  (define (iter bindings)
    (list (make-lambda (list (car (first-binding bindings)))
                       (if (null? (rest-bindings bindings))
                           (let-body exp)
                           (list (iter (rest-bindings bindings))))
                       (cadr (first-binding bindings)))))
  (iter (let-bindings exp)))

結果は、

> (let*->nested-lets '(let* ((x 3) (y (+ x 2)) (z (+ x y 5))) (* x z)))       
(let ((x 3)) (let ((y (+ x 2))) (let ((z (+ x y 5))) (* x z))))               
> (let ((x 3)) (let ((y (+ x 2))) (let ((z (+ x y 5))) (* x z))))             
39                                                                            
> (let*->combination '(let* ((x 3) (y (+ x 2)) (z (+ x y 5))) (* x z)))       
((lambda (x) ((lambda (y) ((lambda (z) (* x z)) (+ x y 5))) (+ x 2))) 3)      
> ((lambda (x) ((lambda (y) ((lambda (z) (* x z)) (+ x y 5))) (+ x 2))) 3) 
39

今見て気づいたけど、bindingsが最初から空だとエラーになるな・・・。
つっても

(let* () (+ 1 1))

みたいなのは使われないよなぁ・・・

ex4.9

この問題やってて気づいたけどmzschemeにはwhileとかdolistとかが無いのか。というか、それが普通なのかしら。
以下に怪しいコード

;;(while <predicate> <bod>)
(put 'eval 'while (lambda (exp env) (eval (expand-while exp) env)))
(define (while-expr exp)
  (cadr exp))
(define (while-body exp)
  (cddr exp))
(define (expand-while exp)
  (sequence->exp (list (list 'define 'while-iter (make-lambda
                                            '()
                                            (list
                                             (make-if (while-expr exp)
                                                      (sequence->exp
                                                       (append
                                                        (while-body exp)
                                                        (list '(while-iter))))
                                                      'true)))) ;;return value
                       (list 'while-iter))))

まぁ動いてるし、いいや…
こんどマクロでの定義例でも見ておこう…

;;; M-Eval input:                                                             
(let 
((count 1) 
(result 1)) 
(while (< count 6) 
(set! result (* count result)) 
(set! count (+ count 1))) 
result)  
                                                                              
;;; M-Eval value:                                                             
120                                                                           
         

とまぁ動いてはいるようだ。

4.10

めんどい。パス。
というか別にやらんでもいい気がする・・・