clojure – 评估者环境中的存储过程导致无限循环

我将
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.

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

  2. 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的名称.

点赞