(sicp48)m4.3~m4.7

今日やった分。

4.3

データ主導の形に変更。gets/putsはてきとうに(putsはわざわざ作らなくてもいいかも)

#|
;;getの中身を作るためのelisp
(defun make-func-mem (beg end) (interactive "r")
  (let ((mem '()))
    (save-excursion
     (goto-char end)
     (while (and (> (point) beg)
                 (search-backward-regexp "\(define \(\\\(.+?\\\)[ \)].*\)" nil t))
       (push (match-string-no-properties 1) mem)))
    (let ((context 
	   (apply #'concat(mapcar (lambda (word)
                                    (concat "(" word " . ,|" word "|) "))
                                  mem))))
      
      (insert (concat "`(" context ")\n")))))
|#


(define (test exp env)
  (print "exp: " exp)
  (print "env: " env))

;;面倒なので、つくってある関数にわたそう
(define (@set! exp env) (eval-assignment exp env))
(define (@define exp env) (eval-definition exp env))
(define (@if exp env) (eval-if exp env))
(define (@lambda exp env)
  (make-procedure (lambda-parameters exp)
                  (lambda-body exp)
                  env))
(define (@begin exp env)
  (eval-sequence (begin-actions exp) env))
(define (@quote exp env) (text-of-quotation exp))
(define (@cond exp env)
  (@eval (cond->if exp) env))

;;((cdr `(test . ,test)) "exp" "env")
;;quasi-quoteを使えばいい
(define get
  (let ((procedures `((test . ,|test|) (@set! . ,|@set!|) (@define . ,|@define|) (@if . ,|@if|) (@lambda . ,|@lambda|) (@begin . ,|@begin|) (@quote . ,|@quote|) (@cond . ,|@cond|) )))
    (lambda (tag)
       (cond ((assoc tag procedures) => cdr)
             (else #f)))))

(define (@eval exp env)
  (cond ((self-evaluating? exp) exp)
        ((variable? exp) (lookup-variable-value exp env))
        ((get (car exp)) => (lambda (f) (f (cdr exp) env)))
        ((application? exp)
         (@apply (@eval (operator exp) env)
                 (list-of-values (operands exp) env)))
        (else
         (error "Unknown expression type -- EVAL" exp))))

4.4

特にむずかしいところは存在しない(途中で処理を終らせたいから、foldとかが使えないくらい)

;;and/or
(define (@and exp env)
  (let loop ((state #f) (seq exp))
    (if (null? seq)
        state
        (let ((first (car seq))
              (rest (cdr seq)))
          (if first
              (loop first rest) ;firstは#f以外の値(#tとはかぎらない)
              #f)))))

;(@and '(#t #t a) 'test)
;; gosh> #?=seq
;; #?-    (#t #t a)
;; #?=seq
;; #?-    (#t a)
;; #?=seq
;; #?-    (a)
;; #?=seq
;; #?-    ()
;; a

(define (@or exp env)
  (let loop ((seq exp))
    (if (null? seq)
        #f
        (let ((first (car seq))
              (rest (cdr seq)))
          (if first
              first
              (loop rest))))))

;; (@or '(#f 2 #f #f) 'test)
;; gosh> #?=seq
;; #?-    (#f 2 #f #f)
;; #?=seq
;; #?-    (2 #f #f)
;; 2

あとは、@evalの中に加えるだけ

(define get
  (let ((procedures `((test . ,|test|) (@set! . ,|@set!|) (@define . ,|@define|) (@if . ,|@if|) (@lambda . ,|@lambda|) (@begin . ,|@begin|) (@quote . ,|@quote|) (@cond . ,|@cond|) (@and . ,|@and|) (@or . ,|@or|) )))
    (lambda (tag)
       (cond ((assoc tag procedures) => cdr)
             (else #f)))))

ifにてんかいしても良かったかもしれない。

4.5

expand-clausesを書き変えれば良さそう。

(define (expand-clauses clauses)
  (if (null? clauses)
      #f
      (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))
            (let ((firsts-actions (cond-actions first)))
              (if (eq? '=> (car firsts-actions))
                  (let ((predicate-value (cond-predicate first))
                        (recipient (cadr firsts-actions)))
                        (make-if predicate-value
                                 (list recipient predicate-value)
                                 (expand-clauses rest)))
                  (make-if (cond-predicate first)
                           (sequence->exp firsts-actions)
                           (expand-clauses rest))))))))

;; (expand-clauses (cond-clauses
;;                  '(@cond ((= n 3) 2)
;;                          ((= n 1) => square)
;;                          (else 0))))
;; gosh> (if (= n 3) 2 (if #0=(= n 1) (square #0#) 0))

4.6

letをlambda式に変更

;lambda <- let
;; '((lambda vars body) exps) <- (let var&exps body)

(define (@let exp env)
  (@apply (let->lambda exp) env))

(define (let->lambda exp)
  (let ((arguments (cadr exp))
        (body (caddr exp)))
    (let ((vars (map car arguments))
         ;; (exps (map (lambda (x) (eval (cadr x) 'interaction-environment)) arguments)))
          (exps (map (lambda (x) (@eval (cadr x) 'local)) arguments)))
      (list (make-lambda vars body) exps))))

;; (map (lambda (x) (eval (cadr x) 'interaction-environment)) '((a (+ 1 2)) (b (* 2 2))))

;; (let->lambda 
;;  '(let ((a (+ 1 2))
;;         (b (* 3 3)))
;;     (+ a b)))
;; gosh> ((lambda (a b) + a b) (3 9))

evalで計算しちゃったらよくないかも

(define (let->lambda exp)
  (let ((arguments (cadr exp))
        (body (caddr exp)))
    (let ((vars (map car arguments))
          (exps (map cadr arguments)))
      (list (make-lambda vars body) exps))))

;; (let->lambda 
;;  '(let ((a (+ 1 2))
;;         (b (* 3 3)))
;;     (+ a b)))
;; gosh> ((lambda (a b) + a b) ((+ 1 2) (* 3 3)))

(define get
  (let ((procedures `((test . ,|test|) (@set! . ,|@set!|) (@define . ,|@define|) (@if . ,|@if|) (@lambda . ,|@lambda|) (@begin . ,|@begin|) (@quote . ,|@quote|) (@cond . ,|@cond|) (@and . ,|@and|) (@or . ,|@or|) (@let . ,|@let|))))
        (lambda (tag)
          (cond ((assoc tag procedures) => cdr)
                (else #f)))))

getって名前だとほかの関数とぶつかりそう。
getのlocal-variableに格納しないで、globalにおいても良いかもしれない
あー,あと、速さを気にするならfor-eachとpush!を使ったほうがいいかも(ループの数をへらせる)
;;こんな感じで

(define (let->lambda exp)
  (let ((arguments (reverse (cadr exp)))  ;;向きをさかさまにしておく
        (body (caddr exp))
        (vars '())
        (exps '()))
    (for-each (lambda (x) (push! vars (car x))
                 (push! exps (cadr x)))
              arguments)
    (list (make-lambda vars body) exps)))

4.7

let*->letによる表現に変更。foldでできそう。

(define (let*->nested-lets exp)
  (let ((vals (cadr exp))
        (body (caddr exp)))
    (fold (lambda (x it)
            (cons 'let (list (list x) it)))
          body
          (reverse vals))))

(let*->nested-lets 
 '(let* ((x 3)
         (y (+ x 2))
         (z (* x y)))
    (+ x y z)))
;; gosh> (let ((x 3)) (let ((y (+ x 2))) (let ((z (* x y))) (+ x y z))))