5

彼得·诺维格(Peter Norvig)的史诗巨著《人工智能编程范式》第 7 章中,他描述了一个函数,该函数实际上是在解释 REPL 中的基本方案时使用interp的一个简单函数。eval

(defun interp (x &optional env)
  "Interpret (evaluate) the expression x in the environment env."
  (cond
    ((symbolp x) (get-var x env))
    ((atom x) x)
    ((case (first x)
       (QUOTE  (second x))
       (BEGIN  (last1 (mapcar #'(lambda (y) (interp y env))
                              (rest x))))
       (SET!   (set-var! (second x) (interp (third x) env) env))
       (IF     (if (interp (second x) env)
                   (interp (third x) env)
                   (interp (fourth x) env)))
       (LAMBDA (let ((parms (second x))
                     (code (maybe-add 'begin (rest2 x))))
                 #'(lambda (&rest args)
                     (interp code (extend-env parms args env)))))
       (t      ;; a procedure application
               (apply (interp (first x) env)
                      (mapcar #'(lambda (v) (interp v env))
                              (rest x))))))))

有趣的是——Christian Queinnec 的 Lisp In Small Pieces的开头章节有一个非常相似的功能,他称之为eval

;;; This is a naive evaluator for Scheme written in naive Scheme.

(define (evaluate e env)
  (if (atom? e) 
      (cond ((symbol? e) (lookup e env))
            ((or (number? e) (string? e) (char? e)
                 (boolean? e) (vector? e) )
             e )
            (else (wrong "Cannot evaluate" e)) )
      (case (car e)
        ((quote)  (cadr e))
        ((if)     (if (evaluate (cadr e) env)
                      (evaluate (caddr e) env)
                      (evaluate (cadddr e) env) ))
        ((begin)  (eprogn (cdr e) env))
        ((set!)   (update! (cadr e) env (evaluate (caddr e) env)))
        ((lambda) (make-function (cadr e) (cddr e) env))
        (else     (invoke (evaluate (car e) env)
                          (evlis (cdr e) env) )) ) ) )

我的问题是 - Clojure 源在哪里是等效eval/interp功能?我假设它在某个地方的阅读器代码中。

4

1 回答 1

6

你的意思是,Clojure 的eval程序是什么?那将是clojure.core/eval。文档中的此链接显示了评估的发生方式:

  • 交互式地,在 REPL
  • 在从流中读取的一系列表单上,通过加载或加载文件
  • 以编程方式,通过eval

如果您对实际的源代码感兴趣,请查看 Clojure 的core.clj文件。特别是,代码eval如下所示:

(defn eval
  "Evaluates the form data structure (not text!) and returns the result."
  [form] (. clojure.lang.Compiler (eval form)))

反过来,类中的eval方法Compiler(在上面的代码片段中引用,并位于Compiler.java文件中)如下所示:

public static Object eval(Object form) throws Exception{
    boolean createdLoader = false;
    if(true)//!LOADER.isBound())
        {
        Var.pushThreadBindings(RT.map(LOADER, RT.makeClassLoader()));
        createdLoader = true;
        }
    try
        {
        Integer line = (Integer) LINE.deref();
        if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
            line = (Integer) RT.meta(form).valAt(RT.LINE_KEY);
        Var.pushThreadBindings(RT.map(LINE, line));
        try
            {
            form = macroexpand(form);
            if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO))
                {
                ISeq s = RT.next(form);
                for(; RT.next(s) != null; s = RT.next(s))
                    eval(RT.first(s));
                return eval(RT.first(s));
                }
            else if(form instanceof IPersistentCollection
                    && !(RT.first(form) instanceof Symbol
                         && ((Symbol) RT.first(form)).name.startsWith("def")))
                {
                FnExpr fexpr = (FnExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form), "eval");
                IFn fn = (IFn) fexpr.eval();
                return fn.invoke();
                }
            else
                {
                Expr expr = analyze(C.EVAL, form);
                return expr.eval();
                }
            }
        finally
            {
            Var.popThreadBindings();
            }
        }
    catch(Throwable e)
        {
        if(!(e instanceof CompilerException))
            throw new CompilerException((String) SOURCE.deref(), (Integer) LINE.deref(), e);
        else
            throw (CompilerException) e;
        }
    finally
        {
        if(createdLoader)
            Var.popThreadBindings();
        }
}

我想这不是你所期望的那样,但考虑到 Clojure 在 JVM 之上运行的事实,评估部分作为 Java 程序而不是 Lisp 程序是有道理的——就像在问题中引用的代码。

于 2012-05-05T13:31:11.947 回答