Last active
November 2, 2018 12:49
-
-
Save LuckyKoala/922f30326f9e9d37aa03c298860154de to your computer and use it in GitHub Desktop.
SICP 4.1,Lisp解释器实现(包含习题解答)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(define (eval exp env) ((analyze exp) env)) | |
(define (analyze exp) | |
(cond ((self-evaluating? exp) | |
(analyze-self-evaluating exp)) | |
((quoted? exp) | |
(analyze-quoted exp)) | |
((variable? exp) | |
(analyze-variable exp)) | |
((assignment? exp) | |
(analyze-assignment exp)) | |
((definition? exp) | |
(analyze-definition exp)) | |
((if? exp) | |
(analyze-if exp)) | |
((lambda? exp) | |
(analyze-lambda exp)) | |
((begin? exp) | |
(analyze-sequence | |
(begin-actions exp))) | |
((cond? exp) | |
(analyze (cond->if exp))) | |
((and? exp) | |
(analyze-and | |
(and-clauses exp))) | |
((or? exp) | |
(analyze-or | |
(or-clauses exp))) | |
((let? exp) | |
(analyze (let->combination exp))) | |
((let*? exp) | |
(analyze (let*->nested-lets exp))) | |
((letrec? exp) | |
(analyze (letrec->let exp))) | |
((do? exp) | |
(analyze (do->combination exp) env)) | |
((application? exp) | |
(analyze-application exp)) | |
(else | |
(error "Unknown expression | |
type: ANALYZE" exp)))) | |
;; ================ | |
;; Analyze part | |
;; ================ | |
(define (analyze-self-evaluating exp) | |
(lambda (env) exp)) | |
(define (analyze-quoted exp) | |
(let ((qval (text-of-quotation exp))) | |
(lambda (env) qval))) | |
(define (analyze-variable exp) | |
(lambda (env) (lookup-variable-value exp env))) | |
;; Assignments and definitions | |
(define (analyze-assignment exp) | |
(let ((avar (assignment-variable exp)) | |
(aproc (analyze (assignment-value exp)))) | |
(lambda (env) | |
(set-variable-value! avar (aproc env) env) | |
'assignment-ok))) | |
(define (analyze-definition exp) | |
(let ((dvar (definition-variable exp)) | |
(dproc (analyze (definition-value exp)))) | |
(lambda (env) | |
(define-variable! dvar (dproc env) env) | |
'definition-ok))) | |
;; Conditionals | |
(define (analyze-if exp) | |
(let ((pproc (analyze (if-predicate exp))) | |
(cproc (analyze (if-consequent exp))) | |
(aproc (analyze (if-alternative exp)))) | |
(lambda (env) | |
(if (true? (pproc env)) | |
(cproc env) | |
(aproc env))))) | |
(define (analyze-lambda exp) | |
(let ((params (lambda-parameters exp)) | |
(body (lambda-body exp))) | |
(lambda (env) (make-procedure params body env)))) | |
;; Sequences | |
(define (analyze-sequence exps) | |
(define (sequentially proc1 proc2) | |
(lambda (env) (proc1 env) (proc2 env))) | |
(define (loop first-proc rest-procs) | |
(if (null? rest-procs) | |
first-proc | |
(loop (sequentially first-proc | |
(car rest-procs)) | |
(cdr rest-procs)))) | |
(let ((procs (map analyze exps))) | |
(if (null? procs) | |
(error "Empty sequence: ANALYZE")) | |
(loop (car procs) (cdr procs)))) | |
;; Application | |
(define (analyze-application exp) | |
(let ((fproc (analyze (operator exp))) | |
(aprocs (map analyze (operands exp)))) | |
(lambda (env) | |
(execute-application | |
(fproc env) | |
(map (lambda (aproc) (aproc env)) | |
aprocs))))) | |
(define (execute-application proc args) | |
(cond ((primitive-procedure? proc) | |
(apply-primitive-procedure proc args)) | |
((compound-procedure? proc) | |
(eval (procedure-body proc) | |
(extend-environment | |
(procedure-parameters proc) | |
args | |
(procedure-environment proc)))) | |
(else (error "Unknown procedure type: | |
EXECUTE-APPLICATION" | |
proc)))) | |
;; And and Or | |
(define (analyze-and exps) | |
(define (iter proc1 proc2) | |
(lambda (env) | |
(cond ((not (true? (proc1 env))) 'false) | |
((not (true? (proc2 env))) 'false) | |
(else 'true)))) | |
(define (loop first-proc rest-procs) | |
(if (null? rest-procs) | |
first-proc | |
(loop (iter first-proc | |
(car rest-procs)) | |
(cdr rest-procs)))) | |
(let ((procs (map analyze exps))) | |
(if (null? procs) | |
(error "Empty and: ANALYZE")) | |
(loop (car procs) (cdr procs)))) | |
(define (analyze-or exps) | |
(define (iter proc1 proc2) | |
(lambda (env) | |
(cond ((true? (proc1 env)) 'true) | |
((true? (proc2 env)) 'true) | |
(else 'false)))) | |
(define (loop first-proc rest-procs) | |
(if (null? rest-procs) | |
first-proc | |
(loop (iter first-proc | |
(car rest-procs)) | |
(cdr rest-procs)))) | |
(let ((procs (map analyze exps))) | |
(if (null? procs) | |
(error "Empty or: ANALYZE")) | |
(loop (car procs) (cdr procs)))) | |
;; ----------------------------------------------- | |
;; the specification of the syntax of our language | |
;; ----------------------------------------------- | |
(define (self-evaluating? exp) | |
(cond ((number? exp) true) | |
((string? exp) true) | |
(else false))) | |
(define (variable? exp) (symbol? exp)) | |
(define (quoted? exp) | |
(tagged-list? exp 'quote)) | |
(define (text-of-quotation exp) | |
(cadr exp)) | |
(define (tagged-list? exp tag) | |
(if (pair? exp) | |
(eq? (car exp) tag) | |
false)) | |
(define (assignment? exp) | |
(tagged-list? exp 'set!)) | |
(define (assignment-variable exp) | |
(cadr exp)) | |
(define (assignment-value exp) (caddr exp)) | |
(define (definition? exp) | |
(tagged-list? exp 'define)) | |
(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) ; formal parameters | |
(cddr exp)))) ; body | |
(define (lambda? exp) | |
(tagged-list? exp 'lambda)) | |
(define (lambda-parameters exp) (cadr exp)) | |
(define (lambda-body exp) (cddr exp)) | |
(define (make-lambda parameters body) | |
(cons 'lambda (cons parameters body))) | |
;;(list 'lambda parameters (sequence->exp body))) | |
(define (if? exp) (tagged-list? exp 'if)) | |
(define (if-predicate exp) (cadr exp)) | |
(define (if-consequent exp) (caddr exp)) | |
(define (if-alternative exp) | |
(if (not (null? (cdddr exp))) | |
(cadddr exp) | |
'false)) | |
(define (make-if predicate | |
consequent | |
alternative) | |
(list 'if | |
predicate | |
consequent | |
alternative)) | |
(define (begin? exp) | |
(tagged-list? exp 'begin)) | |
(define (begin-actions exp) (cdr exp)) | |
(define (sequence->exp seq) | |
(cond ((null? seq) seq) | |
((null? (cdr seq)) (car seq)) | |
(else (make-begin seq)))) | |
(define (make-begin seq) (cons 'begin seq)) | |
(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)) | |
(define (cond? exp) | |
(tagged-list? exp 'cond)) | |
(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))) | |
;;ex4.4 | |
(define (and? exp) | |
(tagged-list? exp 'and)) | |
(define (and-clauses exp) (cdr exp)) | |
(define (or? exp) | |
(tagged-list? exp 'or)) | |
(define (or-clauses exp) (cdr exp)) | |
;;Add an additional syntax for cond clauses (⟨test⟩ => ⟨recipient⟩) | |
;;ex4.5 | |
(define (=>? exp) | |
(eq? (cadr exp) '=>)) | |
(define (cond-recipient exp) | |
(caddr exp)) | |
(define (expand-clauses clauses) | |
(if (null? clauses) | |
'false ; no else clause | |
(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 ((predicate (cond-predicate first))) | |
(make-if predicate | |
(if (=>? first) | |
(cons (cond-recipient first) predicate) | |
(sequence->exp (cond-actions first))) | |
(expand-clauses rest))))))) | |
;;ex 4.6 | |
(define (let? exp) | |
(tagged-list? exp 'let)) | |
(define (let-vars exp) | |
(map car (cadr exp))) | |
(define (let-exps exp) | |
(map cadr (cadr exp))) | |
(define (let-body exp) | |
(cddr exp)) | |
(define (let->combination exp) | |
(if (name-let? exp) | |
(cons (make-lambda '() | |
(list (make-define (name-let-name exp) | |
(make-lambda (let-vars (name-let-remain exp)) | |
(let-body (name-let-remain exp)))) | |
(cons (name-let-name exp) (let-exps (name-let-remain exp))))) | |
'()) | |
(cons (make-lambda (let-vars exp) | |
(let-body exp)) | |
(let-exps exp)))) | |
;;ex4.7 | |
(define (let*? exp) | |
(tagged-list? exp 'let*)) | |
(define (let*-bindings exp) | |
(cadr exp)) | |
(define (let*-body exp) | |
(cddr exp)) | |
(define (make-let bindings body) | |
(list 'let bindings body)) | |
(define (let*->nested-lets exp) | |
(expand-lets (let*-bindings exp) (let*-body exp))) | |
(define (expand-lets bindings body) | |
(if (null? bindings) | |
(sequence->exp body) | |
(make-let (list (car bindings)) | |
(expand-lets (cdr bindings) | |
body)))) | |
;;ex4.8 | |
(define (name-let? exp) | |
(symbol? (cadr exp))) | |
(define (name-let-name exp) | |
(cadr exp)) | |
(define (name-let-remain exp) | |
(cdr exp)) | |
(define (make-define var exp) | |
(list 'define var exp)) | |
;;ex4.9 | |
;; Syntax: | |
;; (do ((variable init step) ...) (test expr ...) commands) | |
;; Example: | |
;; (let ((x '(1 3 5 7 9))) | |
;; (do ((x x (cdr x)) | |
;; (sum 0 (+ sum (car x)))) | |
;; ((null? x) sum))) | |
;; Output: | |
;; 25 | |
(define (do? exp) | |
(tagged-list? exp 'do)) | |
(define (do-vars exp) | |
(map car (cadr exp))) | |
(define (do-inits exp) | |
(map cadr (cadr exp))) | |
(define (do-steps exp) | |
(map caddr (cadr exp))) | |
(define (do-test exp) | |
(caaddr exp)) | |
(define (do-exprs exp) | |
(cdaddr exp)) | |
(define (do-commands exp) | |
(cdddr exp)) | |
(define (do-body exp) | |
(list (make-if (do-test exp) | |
(sequence->exp (do-exprs exp)) | |
(sequence->exp (list (do-commands exp) | |
(cons 'do-iter (do-steps exp))))))) | |
(define (do->combination exp) | |
(cons (make-lambda '() | |
(list (make-define 'do-iter | |
(make-lambda (do-vars exp) | |
(do-body exp))) | |
(cons 'do-iter (do-inits exp)))) | |
'())) | |
;; 4.1.3 Evaluator Data Structures | |
;; Testing of predicates | |
(define (true? x) | |
(not (eq? x false))) | |
(define (false? x) | |
(eq? x false)) | |
;; Representing procedures | |
(define (make-procedure parameters body env) | |
(list 'procedure parameters (sequence->exp (scan-out-defines body)) env)) | |
(define (compound-procedure? p) | |
(tagged-list? p 'procedure)) | |
(define (procedure-parameters p) (cadr p)) | |
(define (procedure-body p) (caddr p)) | |
(define (procedure-environment p) (cadddr p)) | |
;; ex4.16 | |
(define (let-unassigned-bindings bindings) | |
(map (lambda (binding) | |
(list (car binding) '(quote *unassigned))) | |
bindings)) | |
(define (let-assignment-sequence bindings) | |
(map (lambda (binding) | |
(list 'set! (car binding) (cadr binding))) | |
bindings)) | |
(define (scan-out-defines body) | |
(define (let-definition-sequence body) | |
(filter definition? body)) | |
(define (let-rest-sequence body) | |
(filter (lambda (exp) | |
(not (definition? exp))) | |
body)) | |
(let ((bindings (map (lambda (exp) (list (definition-variable exp) (definition-value exp))) | |
(let-definition-sequence body)))) | |
(if (= (length bindings) 0) | |
body | |
(list (make-let (let-unassigned-bindings bindings) | |
(sequence->exp (list (sequence->exp (let-assignment-sequence bindings)) | |
(sequence->exp (let-rest-sequence body))))))))) | |
;; ex4.20 | |
(define (letrec? exp) | |
(tagged-list? exp 'letrec)) | |
(define (letrec-bindings exp) | |
(cadr exp)) | |
(define (letrec-body exp) | |
(cddr exp)) | |
(define (letrec->let exp) | |
(let ((bindings (letrec-bindings exp))) | |
(make-let (let-unassigned-bindings bindings) | |
(sequence->exp (list (sequence->exp (let-assignment-sequence bindings)) | |
(sequence->exp (letrec-body exp))))))) | |
;; ex4.21 | |
;; It is indeed possible to specify recursive procedures without using letrec (or even 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)))))) | |
|# | |
;; Operations on Environments(a pair of lists) | |
(define (enclosing-environment env) (cdr env)) | |
(define (first-frame env) (car env)) | |
(define the-empty-environment '()) | |
(define (make-frame variables values) | |
(cons variables values)) | |
(define (frame-variables frame) (car frame)) | |
(define (frame-values frame) (cdr frame)) | |
(define (add-binding-to-frame! var val frame) | |
(set-car! frame (cons var (car frame))) | |
(set-cdr! frame (cons val (cdr frame)))) | |
(define (extend-environment vars vals base-env) | |
(if (= (length vars) (length vals)) | |
(cons (make-frame vars vals) base-env) | |
(if (< (length vars) (length vals)) | |
(error "Too many arguments supplied" | |
vars | |
vals) | |
(error "Too few arguments supplied" | |
vars | |
vals)))) | |
(define (lookup-variable-value var env) | |
(define (env-loop env) | |
(define (scan vars vals) | |
(cond ((null? vars) | |
(env-loop | |
(enclosing-environment env))) | |
((eq? var (car vars)) | |
(let ((val (car vals))) | |
(if (eq? val '*unassigned*) | |
(error "Attempt is made to use the value of the not-yet-assigned variable") | |
val))) | |
(else (scan (cdr vars) | |
(cdr vals))))) | |
(if (eq? env the-empty-environment) | |
(error "Unbound variable" var) | |
(let ((frame (first-frame env))) | |
(scan (frame-variables frame) | |
(frame-values frame))))) | |
(env-loop env)) | |
(define (set-variable-value! var val env) | |
(define (env-loop env) | |
(define (scan vars vals) | |
(cond ((null? vars) | |
(env-loop | |
(enclosing-environment env))) | |
((eq? var (car vars)) | |
(set-car! vals val)) | |
(else (scan (cdr vars) | |
(cdr vals))))) | |
(if (eq? env the-empty-environment) | |
(error "Unbound variable: SET!" var) | |
(let ((frame (first-frame env))) | |
(scan (frame-variables frame) | |
(frame-values frame))))) | |
(env-loop env)) | |
(define (define-variable! var val env) | |
(let ((frame (first-frame env))) | |
(define (scan vars vals) | |
(cond ((null? vars) | |
(add-binding-to-frame! | |
var val frame)) | |
((eq? var (car vars)) | |
(set-car! vals val)) | |
(else (scan (cdr vars) | |
(cdr vals))))) | |
(scan (frame-variables frame) | |
(frame-values frame)))) | |
;; 4.1.4 Running the Evaluator as a Program | |
(define (setup-environment) | |
(let ((initial-env | |
(extend-environment | |
(primitive-procedure-names) | |
(primitive-procedure-objects) | |
the-empty-environment))) | |
(define-variable! 'true true initial-env) | |
(define-variable! 'false false initial-env) | |
initial-env)) | |
(define (primitive-procedure? proc) | |
(tagged-list? proc 'primitive)) | |
(define (primitive-implementation proc) | |
(cadr proc)) | |
(define primitive-procedures | |
(let ((f-cons | |
(list | |
(list 'car car) | |
(list 'cdr cdr) | |
(list 'cons cons) | |
(list 'null? null?) | |
(list 'list list) | |
(list 'append append) | |
(list 'cadr cadr) | |
(list 'caddr caddr))) | |
(f-oper | |
(list | |
(list '= =) | |
(list '+ +) | |
(list '- -) | |
(list '* *) | |
(list '/ /))) | |
(f-pred | |
(list | |
(list 'eq? eq?) | |
(list 'pair? pair?) | |
(list 'null? null?) | |
(list 'number? number?) | |
(list 'symbol? symbol?) | |
(list 'string? string?))) | |
(f-print | |
(list | |
(list 'display display) | |
(list 'error error) | |
(list 'newline newline))) | |
(f-stream | |
(list | |
(list 'map map)))) | |
(append f-cons f-oper f-pred f-print f-stream))) | |
(define (primitive-procedure-names) | |
(map car primitive-procedures)) | |
(define (primitive-procedure-objects) | |
(map (lambda (proc) | |
(list 'primitive (cadr proc))) | |
primitive-procedures)) | |
(define (apply-primitive-procedure proc args) | |
(apply | |
(primitive-implementation proc) args)) | |
(define the-global-environment | |
(setup-environment)) | |
;; REPL | |
(define input-prompt ";;; M-Eval input:") | |
(define output-prompt ";;; M-Eval value:") | |
(define (driver-loop) | |
(prompt-for-input input-prompt) | |
(let ((input (read))) | |
(if (eq? input 'exit) | |
'exit | |
(let ((output | |
(eval input | |
the-global-environment))) | |
(announce-output output-prompt) | |
(user-print output) | |
(driver-loop))))) | |
(define (prompt-for-input string) | |
(newline) (newline) | |
(display string) (newline)) | |
(define (announce-output string) | |
(newline) (display string) (newline)) | |
(define (user-print object) | |
(if (compound-procedure? object) | |
(display | |
(list 'compound-procedure | |
(procedure-parameters object) | |
(procedure-body object) | |
'<procedure-env>)) | |
(display object))) | |
;; Input (driver-loop) to start driver loop | |
;; Using functions from underlying Scheme implementation: | |
#| | |
list, cons, car, cdr (include cadr caddr etc.) | |
function, let -> enviroment, lambda and function application | |
cond -> if | |
number?, string?, null?, symbol?, eq?, pair? | |
display, error, newline | |
quote (include "'" shorthand for quote) | |
map | |
apply | |
|# | |
;; TODO | |
;; Reduce usage of functions import from underlying scheme | |
;; Add test case |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment