评估程序环境中的存储过程导致无限循环

Storing procedure in environment of evaluator leads to infinite loop

我转换了 Structure and Interpretation of Computer Programs (SICP) version of the meta-circular evaluator to Clojure. The main difference (besides syntax) is the handling of the environment structure. Since you cannot use set-car! and set-cdr! in Clojure, these are implemented via an atom holding a map (copied from the code of Greg Sexton's chapter 4 notes on GitHub,我认为它与无法定义过程有同样的问题。

两个评估者的代码可以在这里找到:

不幸的是,定义过程无法正常工作。我期望发生的是:

;;; M-Eval input:
(defn (add1 x) (+ x 1))

;;; M-Eval value:
< Environment printed >

;;; M-Eval input:
(add1 10)

;;; M-Eval value:
11

注:除了define叫defn外,基本都是Scheme代码输入。

此处定义 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)

我得到一个很长的环境输出(看起来创建的过程有一个包含过程本身的环境),然后是 WhosebugError。

  1. Unhandled clojure.lang.Compiler$CompilerException
    Error compiling:
    scheme-evaluator.clj:316:1
    (...)

  2. Caused by java.lang.WhosebugError
    (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-body 程序。 lambda 列表包含 3 个元素;标记、参数及其主体。然而,通过 lambda-body 检索正文使用 cddr 而不是 caddr,因此结果被额外的列表包裹。因此,如果您像这样更改 lambda-body 的定义:

(defn lambda-body [exp] (third exp))

然后就可以计算出结果了。

注意:如果你想避免堆栈溢出错误,那么你可以将 eval-definitiondefine-variable! 更改为 return 其他内容,例如给定的名称 exp .