我将
Structure and Interpretation of Computer Programs (SICP)版的元循环评估器转换为Clojure.主要区别(除了语法)是环境结构的处理.既然你不能用套车!和set-cdr!在Clojure中,这些是通过一个持有地图的原子来实现的(从
Greg Sexton’s chapter 4 notes on GitHub的代码中复制,我认为它具有同样的问题而无法定义过程).
可在此处找到两个评估者的代码:
> SICP Evaluator Scheme
> Converted Evaluator Clojure
不幸的是,定义一个过程不能正常工作.我期望发生的是:
;;; M-Eval input:
(defn (add1 x) (+ x 1));;; M-Eval value:
< Environment printed >;;; M-Eval input:
(add1 10);;; M-Eval value:
11
注意:它基本上是输入的Scheme代码,除了define之外,它被称为defn.
这里定义add1在环境结构中存储一个过程,当用它(add1 10)调用它时,在环境中查找符号add1,并将求值程序创建的过程应用于10,结果为11.
但我看到的是:
;;; M-Eval input:
(defn (add1 x) (+ x 1));;; M-Eval value:
{add1 (procedure (x) (((+ x 1))) (#object[clojure.lang.Atom 0x7c197838 {:status :ready, :val {add1 (procedure (x) (((+ x 1))) (#object[clojure.lang.Atom 0x7c197838 {:status :ready, :val {add1 (procedure (x) (((+ x 1))) (#object[clojure.lang.Atom 0x7c197838 {:status :ready, :val {add1 (procedure (x) (((+ x 1))) (#object[clojure.lang.Atom 0x7c197838 {:status :ready, :val {add1 (procedure (x) (((+ x 1))) (… et cetera)
我得到一个非常长的环境输出(看起来创建的过程有一个包含过程本身的环境),然后是StackOverflowError.
- Unhandled clojure.lang.Compiler$CompilerException
Error compiling:
scheme-evaluator.clj:316:1
(…)- Caused by java.lang.StackOverflowError
(No message)
为清楚起见,我把eval放在下面.但我认为the whole code需要运行才能正确看出问题所在.
(defn eval [exp env]
(cond (self-evaluating? exp) exp
(variable? exp) (lookup-variable-value exp env)
(quoted? exp) (text-of-quotation exp)
(assignment? exp) (eval-assignment exp env)
(definition? exp) (eval-definition exp env)
(if? exp) (eval-if exp env)
(lambda? exp) (make-procedure (lambda-parameters exp)
(lambda-body exp)
env)
(begin? exp) (eval-sequence (begin-actions exp) env)
(cond? exp) (eval (cond->if exp) env)
(application? exp) (apply (eval (operator exp) env)
(list-of-values (operands exp) env))
:else (throw (Throwable. (str "Unknown expression type \"" exp "\" -- EVAL")))))
我希望有人可以帮助我解决这个问题,也许可以对这里出了什么问题有所启发.
最佳答案 问题是lambda体程序. lambda列表包含3个元素;标签,参数和它的主体.然而,通过lambda-body检索正文使用cddr而不是caddr,因此结果被额外列表包装.所以如果你像这样改变lambda体的定义:
(defn lambda-body [exp] (third exp))
然后你可以得到结果计算.
注意:如果你想避免堆栈溢出错误,那么你可以改变eval-definition或define-variable!返回其他内容,例如给定exp的名称.