跟踪一个递归求值器

我用 Moonscript Lua 编写了一个简单的 Lisp 解释器。求值器 如下所示:

eval = ( env, expr ) ->
    if is_symbol expr
        lookup env, expr
    elseif is_define expr
        eval_define env, expr
    elseif is_lambda expr
        eval_lambda env, expr
    else call (map (partial eval, env), expr)

它很好用。 但现在我想要追踪这个过程,并以类似以下这样的方式显示出来:

(+ (+ a b) (+ a c))

(+ (+ 1 2) (+ 1 4))

(+ 3 5)

8

问题在于,由于求值过程是递归的,因此在任何时候我都没有整个表达式可以打印出来。

我必须用命令式的风格重写求值器吗,还是我遗漏了明显的东西?

点赞
用户124319
用户124319

实际跟踪

通常,您希望跟踪代码中实际发生的情况。这是您功能的重写以及跟踪工具可以做的示例:

(defun normal-eval (form env)
  (etypecase form
    (cons (destructuring-bind (op . args) form
            (apply op
                   (mapcar (lambda (u)
                             (normal-eval u env))
                           args))))
    (null nil)
    (symbol (cdr (assoc form env)))
    (t form)))

> (trace normal-eval)
> (normal-eval '(+ (+ 1 3 a) 2) '((a . 5)))

0: (NORMAL-EVAL (+ (+ 1 3 A) 2) ((A . 5)))
  1: (NORMAL-EVAL (+ 1 3 A) ((A . 5)))
    2: (NORMAL-EVAL 1 ((A . 5)))
    2: NORMAL-EVAL returned 1
    2: (NORMAL-EVAL 3 ((A . 5)))
    2: NORMAL-EVAL returned 3
    2: (NORMAL-EVAL A ((A . 5)))
    2: NORMAL-EVAL returned 5
  1: NORMAL-EVAL returned 9
  1: (NORMAL-EVAL 2 ((A . 5)))
  1: NORMAL-EVAL returned 2
0: NORMAL-EVAL returned 11

期望的跟踪

据我所知,使用您提供的代码无法轻松获得所需的输出。但是,如果您愿意改变您的代码,您可以以纯粹的函数方式获得所需的跟踪,只需逐步重写该术语即可。然而,您必须防止评估已经评估的术语,以便逐步改变形式。

(defun s-eval (x env)
  (etypecase x
    (cons (destructuring-bind (new-list . some-evalp)
              (reduce
               (lambda (element R)
                 (destructuring-bind (rec-list . some-evalp) R
                   (multiple-value-bind (value evalp) (s-eval element env)
                     (cons (list* value rec-list)
                           (or some-evalp evalp)))))
               (rest x)
               :from-end t
               :initial-value (cons nil nil))
            (values
             (if some-evalp
                 ;; a least one element required some work
                 ;; so we return the modified term.
                 (cons (first x) new-list)
                 ;; all elements are literal, we can actually
                 ;; replace this form by its evaluation
                 (apply (first x) new-list))
             T)))
    (null (values nil nil))
    (symbol (values (cdr (assoc x env)) t))
    (t (values x nil))))

(defun step-eval (form &optional env)
  (print form)
  (multiple-value-bind (value evalp)
      (s-eval form env)
    (if evalp
        (step-eval value env)
        value)))

> (step-eval '(+ (+ 1 3 a) 2) '((a . 5)))

(+ (+ 1 3 A) 2)
(+ (+ 1 3 5) 2)
(+ 9 2)
11

> (step-eval '(+ (+ 1 3 a) (* b a)) '((a . 5) (b . 0)))

(+ (+ 1 3 A) (* B A))
(+ (+ 1 3 5) (* 0 5))
(+ 9 0)
9

> (step-eval '(+ (+ a b) (+ a c)) '((a . 1)
                                  (b . 2)
                                  (c . 4)))

(+ (+ A B) (+ A C))
(+ (+ 1 2) (+ 1 4))
(+ 3 5)
8

S-EVAL 在环境中评估表单并返回两个值:表单的评估和一个布尔值,指示是否实际发生了一些评估或者该术语是自我评估(文字)。此布尔值用于防止转换子术语通过递归评估已经转换了的术语。STEP-EVAL 在调用自身递归评估,直到评估中止之前打印表单并调用 S-EVAL

2016-07-25 12:23:03