exercise 4.19~21

ex4.19

・・・実行順序、というかdefineの書く順番を変えれば、それっぽく動かすことも時にはできるだろうが、一般的な手法は私にはさぱーりです・・・。

ex4.20

a

(define (letrec-bindings exp)
  (cadr exp))
(define (letrec-body exp)
  (cddr exp))
(define variable-names '(*a* *b* *c* *d* *e* *f* *g*))
(define (letrec->derived-expression exp)
  (define (pick-up-element items n)
;;srfi-1でtakeってのがあるのにあとで気づいた。これはいい車輪の再発明orz
    (if (= n 0)
        '()
        (cons (car items) (pick-up-element (cdr items) (- n 1)))))
  (let* ((body (letrec-body exp))
         (bindings (letrec-bindings exp))
         (vars (map car bindings))
         (vals (map cadr bindings))
         (alps (pick-up-element variable-names (length vars))))
    (make-let
     (map (lambda (var) (list var ''*unassigned*)) vars)
     (append
      (list
       (make-let
        (map (lambda (alp val) (list  alp val)) alps vals)
        (map (lambda (var alp) (list 'set! var alp)) vars alps)))
      body))))

(put 'eval 'letrec (lambda (exp env) (eval (letrec->derived-expression exp) env)))

でおkだが冗長だな・・・
某所の解答見るとかなり簡潔でしょぼーん


以下実行結果

;;; M-Eval input:
(define (f x)
  (letrec ((even? 
            (lambda (n)
              (if (= n 0)
                  true
                  (odd? (- n 1)))))
           (odd?
            (lambda (n)
              (if (= n 0)
                  false
                  (even? (- n 1))))))
    (even? x)))


;;; M-Eval value:
ok

;;; M-Eval input:
(f 4)

;;; M-Eval value:
#t

;;; M-Eval input:
(f 5)

;;; M-Eval value:
#f

b
letで関数束縛すると、スコープの関係上、再帰でもめる。
letrecだと、束縛される関数本体が評価される環境が違うので、再帰とかも上手くいく。
あー、言葉でうまく説明できん。これは図を書きゃ一発なのに。
まぁなにはともあれ、letrec, let , let*とかの違いが分かって(?)よかった。

ex4.21

どうみたってY Combinator
以前他の場所でこういうのを見たときは何がいいのかわからんかったが、
letrecとかdefineをつかわんで、再帰が書けるってのは面白いと思った。
ちなみに解答は

(define (f x)
  ((lambda (even? odd?)
     (even? even? odd? x))
   (lambda (ev? od? n)
     (if (= n 0) true (od? ev? od? (- n 1))))
   (lambda (ev? od? n)
     (if (= n 0) false (ev? ev? od? (- n 1))))))

というか深く考えなくても、ヒント多すぎるので、勘で解けちゃうぞ・・・