2014年9月8日月曜日

開発環境

計算機プログラムの構造と解釈[第2版](ハロルド エイブルソン (著)、ジュリー サスマン (著)、ジェラルド・ジェイ サスマン (著)、Harold Abelson (原著)、Julie Sussman (原著)、Gerald Jay Sussman (原著)、和田 英一 (翻訳)、翔泳社、原書: Structure and Interpretation of Computer Programs (MIT Electrical Engineering and Computer Science)(SICP))の4(超言語的抽象)、4.1(超循環評価器)、4.1.2(式の表現)、導出された式、問題 4.9.を解いてみる。

その他参考書籍

問題 4.9.

コード(BBEdit, Emacs)

sample9.scm

#!/usr/bin/env gosh
;; -*- coding: utf-8 -*-

(define operations '())
(define (put op proc)
  (cons (list op proc) operations))
(define (get op)
  (define (inner operations)
    (if (null? operations)
        #f
        (let ((pair (car operations)))
          (if (eq? op (car pair))
              (cadr pair)
              (inner (cdr operations))))))
  (inner operations))
  
(define (eval exp env)
  (define (self-evaluating? exp)
    (cond ((number? exp) #t)
          ((string? exp) #t)
          (else #f)))
  (define (variable? exp) (symbol? exp))
  (define (application? exp) (pair? exp))
  (define (operator exp) (car exp))
  (define (operands exp) (cdr exp))
  (define (no-operands? ops) (null? ops))
  (define (first-operand ops) (car ops))
  (define (rest-operands ops) (cdr ops))
  
  (cond ((self-evaluating? exp) exp)
        ((variable? exp) (lookup-variable-value exp env))
        ((get (car exp))
         ((get (car exp)) exp env))
        ((application? exp)
         (apply (eval (operator exp) env)
                (list-of-values (operands exp) env)))
        (else
         (error "Unknown expression type -- EVAL" exp))))

(define (apply procedure arguments)
  (cond ((primitive-procedure? procedure)
         (apply-primitive-procedure procedure arguments))
        ((compound-procedure? procedure)
         (eval-sequence
          (procedure-body procedure)
          (extend-environment
           (procedure-parameters procedure)
           arguments
           (procedure-environment procedure))))
        (else
         (error
          "Unknown procedure type -- APPLY" procedure))))

(define (install-quoted-package)
  (define (text-of-quotation exp env) (cadr exp))
  
  (put 'quote text-of-quotation))

(define (install-assignment-package)
  (define (eval-assignment exp env)
    (set-variable-value! (assignment-variable exp)
                         (eval (assignment-value exp) env)
                         env)
    'ok)
  (define (assignment-variable exp) (cadr exp))
  (define (assignment-value exp) (caddr exp))

  (put 'set! eval-assignment))

(define (install-definition-package)
  (define (eval-definition exp env)
    (define-variable! (definition-variable exp)
      (eval (definition-value exp) env)
      env)
    'ok)
  (define (definition-variable exp)
    (if (symbol? (cadr exp))
        (cadr exp)
        (caadr exp)))
  (define (definition-value exp)
    (if (symbol? (cadr exp))
        (caddr exp)
        (make-lambda (cdadr exp)
                     (cddr exp))))
  (define make-lambda (get 'make-lambda))

  (put 'define eval-definition))

(define (install-if-package)
  (define (eval-if exp env)
    (if (true? (eval (if-predicate exp) env))
        (eval (if-consequent exp) env)
        (eval (if-alternative exp) env)))
  (define (true? a) (eq? a #t))
  (define (if-predicate exp) (cadr exp))
  (define (if-consequent exp) (caddr exp))
  (define (if-alternative exp)
    (if (not (null? (cdddr exp)))
        (cadddr exp)
        #f))
  (define (make-if predicate consequent alternative)
    (list 'if predicate consequent alternative))

  (put 'make-if make-if)
  (put 'if eval-if))

(define (install-lambda-package)
  (define (lambda-parameters exp) (cadr exp))
  (define (lambda-body exp) (cddr exp))
  (define (make-lambda parameters body)
    (cons 'lambda (cons parameters body)))

  (put 'make-lambda make-lambda)
  (put 'lambda (lambda (exp env)
                 (make-procedure (lambda-parameters exp)
                                 (lambda-body exp)
                                 env))))

(define (install-begin-package)
  (define (eval-sequence exps env)
    (cond ((last-exp? exps) (eval (first-exp exps) env))
          (else (eval (first-exp exps) env)
                (eval-sequence (rest-exps exps) env))))
  (define (begin-actions exp) (cdr exp))
  (define (last-exp? seq) (null? (cdr seq)))
  (define (first-exp seq) (car seq))
  (define (rest-seq) (cdr seq))
  (define (sequence->exp seq)
    (cond ((null? seq) seq)
          ((last-exp? seq) (first-exp seq))
          (else (make-begin seq))))
  (define (make-begin seq) (cons 'begin seq))

  (put 'sequence->exp sequence->exp)
  (put 'make-begin make-begin)
  (put 'begin (lambda (exp env)
                (eval-sequence (begin-actions exp) env))))

(define (instll-cond-package)
  (define (cond-clauses exp) (cdr exp))
  (define (cond-else-clause? clause)
    (eq? (cond-predicate clause) 'else))
  (define (cond-predicate clause) (car clause))
  (define (cond-actions clause) (cdr clause))
  (define (cond->if exp)
    (expand-clauses (cond-clauses exp)))
  (define (expand-clauses clauses)
    (if (null? clauses)
        'false
        (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))
              (make-if (cond-predicate first)
                       ;; 問題 4.5 拡張構文を使えるようにする
                       (if (eq? (cadr first) '=>)
                           (list (caddr first) (cond-predicate first))
                           (sequence->exp (cond-actions first)))
                       (expand-clauses rest))))))
  (define sequence->exp (get 'sequence->exp))
  (define make-if (get 'make-if))
  (put 'cond (lambda (exp env)
               (eval (cond->if exp) env))))

;; 問題 4.4
(define (install-and-package)
  (define (eval-and exp env)
    (if (null? (cdr exp))
        #t
        (eval-sequence (cdr seq) env)))
  (define (eval-sequence seq env)
    (cond ((null? (cdr seq))
           (if (eval (car seq) env)
               (car seq)
               #f))
          ((eval (car seq) env)
           (eval-sequence (cdr seq) env))
          (else #f)))

  (put 'and eval-and))

(define (install-or-package)
  (define (eval-or exp env)
    (if (null? (cdr exp))
        #f
        (eval-sequence (cdr seq) env)))
  (define (eval-sequence seq env)
    (cond ((null? seq) #f)
          ((eval (car seq) env) (car seq))
          (else
           (eval-sequence (cdr seq) env))))

  (put 'or 'eval-or))

;; 問題 4.6
(define (install-let-package)
  (define (let-clauses exp) (cdr exp))
  (define (let-vars-exps clauses) (car clauses))
  (define (let-vars seq)
    (if (null? (cdr seq))
        (list (caar seq))
        (cons (caar seq)
              (let-vars (cdr seq)))))
  (define (let-exps seq)
    (if (null? (cdr seq))
        (list (cadar seq))
        (cons (cadar seq)
              (let-exps (cdr seq)))))
  (define (let-body clauses) (cdr clauses))
  ;; 問題 4.8
  (define (let->combination exp)
    (let ((clauses (let-clauses exp)))
      (if (pair? (car clauses))
          (let ((seq (let-vars-exps clauses)))
            (let ((vars (let-vars seq))
                  (exps (let-exps seq))
                  (body (let-body seq)))
              (cons (make-lambda vars body) exps)))
          (let ((var (car clauses))
                (bindings (cadr clauses))
                (body (cddr clauses)))
            (let ((vars (let-vars bindings))
                  (exps (let-exps bindings)))
              (sequence->exp
               (list 'define var (make-lambda vars body))
               (cons var exps)))))))
  (define sequence->exp (get 'sequence->exp))
  (define make-lambda (get 'make-lambda))
  (define (make-vars-exps vars exps)
    (if (null? (cdr vars))
        (list (list (car vars) (car exps)))
        (cons (list (car vars) (car exps))
              (make-vars-exps (cdr vars) (cdr exps)))))
  ;; 問題 4.7のlet*で使うために追加
  (define (make-let vars exps body)
    (list 'let
          (make-vars-exps vars exps)
          body))
  (put 'make-let make-let)
  (put 'let (lambda (exp env)
              (let->combination exp))))

;; 問題 4.7
(define (install-let*-package)
  (define (let*-clauses exp) (cdr exp))
  (define (let*-vars-exps clauses) (car clauses))
  (define (let*-vars seq)
    (if (null? (cdr seq))
        (list (caar seq))
        (cons (caar seq)
              (let*vars (cdr seq)))))
  (define (let*-exps seq)
    (if (null? (cdr seq))
        (list (cadar seq))
        (cons (cadar seq)
              (let*-exps (cdr seq)))))
  (define (let*-body clauses) (cdr clauses))
  (define (let*->nested-lets exp)
    (let ((clauses (let*-clauses exp)))
      (let ((vars-exps (let*-vars-exps clauses))
            (body (let*-body clauses)))
        (let ((vars (let*-vars vars-exps))
              (exps (let*-exps vars-exps)))
          (expand vars exps body)))))
  (define (expand vars exps body)
    (if (null? (cdr vars))
        (make-let vars exps body)
        (make-let (list (car vars))
                  (list (car exps))
                  (expand (cdr vars)
                          (cdr exps)
                          body))))
  (define make-let (get 'make-let))

  (put 'let* (lambda (exp env)
               (let*-nested-lets exp))))

;; 問題 4.9 反復構造
(define (install-while-package)
  ;; make-ifとmake-beginから導出
  (define (eval-while exp env)
    (make-if (while-predicate exp)
             (make-begin (append (while-body exp)
                                 (list (eval-while exp env))))
             '()))
  (define (while-predicate exp) (cadr exp))
  (define (while-body exp) (caddr exp))
  (define make-if (get 'make-if))
  (define make-begin (get 'make-begin))

  (put 'while eval-while))

;; 使い方
;; (define i 0)
;; (while (< i 10)
;;        (list (print i)
;;              (set! i (+ i 1))))
;; 
;; output
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
;; 6
;; 7
;; 8
;; 9

(install-quoted-package)
(install-assignment-package)
(install-definition-package)
(install-if-package)
(install-lambda-package)
(install-begin-package)
(instll-cond-package)
(install-and-package)
(install-or-package)
(install-let-package)
(install-let*-package)
(install-while-package)

入出力結果(Terminal(gosh), REPL(Read, Eval, Print, Loop))

$ ./sample9.scm
$

0 コメント:

コメントを投稿