是否有使用正态顺序评估的方案解释器?

发布于 2024-09-08 20:23:39 字数 1062 浏览 12 评论 0原文

我一直在慢慢地完成计算机的结构和解释中的练习程序1.1.5 节讨论了应用评估与正态评估,这个话题在随后的文本中多次出现。由于解释器使用应用顺序求值,因此只需在代码中插入 display 调试语句即可轻松查看其工作原理。能够对正常顺序评估做同样的事情将有助于我的理解。

有谁知道使用正常顺序评估而不是应用顺序实现的Scheme(或Lisp)解释器?

更新:

这是一个根据 SICP 中给出的示例修改的简短示例。我将定义自己的 add 过程来打印参数,并使用书中的 square 过程。

(define (add x y)
  (display x)
  (display y)
  (newline)
  (+ x y))

(define (square x) (* x x))

现在,如果我使用应用顺序评估运行短程序 (square (add 1 2)) ,则 (add 1 2) 的结果将仅计算一次然后通过到 square 过程。操作数 12 应在最终结果之前打印一次。我们可以在解释器中运行它来验证是否发生了这种情况。

> (square (add 1 2))
12
9

但是,使用正常顺序求值时,应将单个操作数 (add 1 2) 复制到 square 过程中,该过程的求值结果为 (* (add 1 2)(添加 1 2))。操作数 12 应在最终结果之前打印两次。

我希望能够在执行正常顺序评估的解释器中运行它,以验证这确实是它的工作原理。

I've been slowly working my way though the exercises in Structure and Interpretation of Computer Programs. Section 1.1.5 talks about applicative vs. normal-order evaluation, and the topic has come up several times in the text afterward. Since the interpreter uses applicative-order evaluation, it's easy to just insert display debug statements in your code to see exactly how it works. It would help my understanding to be able to do the same thing for normal-order evaluation.

Does anyone know of a Scheme (or Lisp) interpreter that's implemented using normal-order evaluation instead of applicative-order?

Update:

Here's a short example modified from the one given in SICP. I'll define my own add procedure to print out the arguments, and use the square procedure from the book.

(define (add x y)
  (display x)
  (display y)
  (newline)
  (+ x y))

(define (square x) (* x x))

Now if I run the short program (square (add 1 2)) using applicative-order evaluation, the result of (add 1 2) will only be computed once then passed to the square procedure. The operands 12 should be printed once before the final result. We can just run this in an interpreter to verify that this is what happens.

> (square (add 1 2))
12
9

Using normal-order evaluation, though, the single operand (add 1 2) should be copied into the square procedure, which would evaluate as (* (add 1 2) (add 1 2)). The operands 12 should be printed twice before the final result.

I'd like to be able to run this in an interpreter that does normal-order evaluation to verify that this is indeed how it works.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

温柔女人霸气范 2024-09-15 20:23:39

事实证明,Scheme 实际上已经附带了一个本质上是正态顺序求值器的东西。它们是您可能听说过很多次的传说中的宏,我们可以重写第 1.1.4--1.1.5 节的示例,以相当容易地使用宏扩展代替过程应用程序:

(define (print . items)
  (for-each display items))

(define-macro (add x y)
  `(begin (print "[ADD " ',x " " ',y "]")
          (+ ,x ,y)))

(define-macro (mul x y)
  `(begin (print "[MUL " ',x " " ',y "]")
          (* ,x ,y)))

(define-macro (square x)
  `(begin (print "[SQUARE " ',x "]")
          (mul ,x ,x)))

(define-macro (sum-of-squares x y)
  `(begin (print "[SUM-OF-SQUARES " ',x " " ',y "]")
          (add (square ,x) (square ,y))))

(define-macro (f a)
  `(begin (print "[F " ',a "]")
          (sum-of-squares (add ,a 1) (mul ,a 2))))

忽略 PRINT,它们的逻辑是有点超出了您在文本中的位置,但它们只是许多显示的简写。实际上,您可能希望完全放弃打印跟踪,而转而使用系统的宏扩展功能。但这因实现而异(例如,在 Ypsilon 中,您将使用 (macro-expand '(f 5)))。

如果您加载这些定义(需要注意的是 DEFINE-MACRO 是非标准的,但这在实践中不应该成为问题,因为大多数方案都提供它),然后像下面这样评估 (f 5)这本书确实会打印出来(当然我稍微修饰了一下):

[F 5]
[SUM-OF-SQUARES (add 5 1) (mul 5 2)]
[ADD (square (add 5 1))         (square (mul 5 2))]
     [SQUARE (add 5 1)]
     [MUL (add 5 1) (add 5 1)]
          [ADD 5 1]
                    [ADD 5 1]
                                [SQUARE (mul 5 2)]
                                [MUL (mul 5 2) (mul 5 2)]
                                     [MUL 5 2]
                                               [MUL 5 2]
136

这或多或少是这本书所说明的过程应该是的。


编写这些类型的宏基本上就像编写正常的过程一样,只不过

  • 您使用的是 DEFINE-MACRO,而不是 DEFINE。
  • 主体上没有隐式 BEGIN,因此如果有多个表达式,则需要提供自己的 BEGIN。
  • 整个身体表情都以沉重的口音为前缀。
  • 参数的所有实例都以逗号为前缀。

这就是编写方案宏 101。

总而言之,在 SICP 的第一章中向某人添加宏有点愚蠢。但是,如果您说修改 Racket 来实现您想要的功能非常困难(并且有些人根本不使用 Racket),那么这里有一个替代方案。

Turns out Scheme actually comes with what is essentially a normal-order evaluator already. They're those fabled macros you've probably heard so much about, and we can rewrite the examples of sections 1.1.4--1.1.5 to use macro expansion in lieu of procedure application rather easily:

(define (print . items)
  (for-each display items))

(define-macro (add x y)
  `(begin (print "[ADD " ',x " " ',y "]")
          (+ ,x ,y)))

(define-macro (mul x y)
  `(begin (print "[MUL " ',x " " ',y "]")
          (* ,x ,y)))

(define-macro (square x)
  `(begin (print "[SQUARE " ',x "]")
          (mul ,x ,x)))

(define-macro (sum-of-squares x y)
  `(begin (print "[SUM-OF-SQUARES " ',x " " ',y "]")
          (add (square ,x) (square ,y))))

(define-macro (f a)
  `(begin (print "[F " ',a "]")
          (sum-of-squares (add ,a 1) (mul ,a 2))))

Ignore the PRINTs, their logic is a little beyond where you're at in the text, but they're just a shorthand for lots of DISPLAYs. Actually, you would want to entirely forgo the tracing-by-printing in favor of using the system's macro-expansion function. but this varies with the implementation (e.g. in Ypsilon you would use (macro-expand '(f 5))).

If you load these definitions (with the caveat that DEFINE-MACRO is non-standard, but that shouldn't be a problem in practice since most Schemes provide it), then evaluating (f 5) like the book does will print out (of course I prettied it up a little):

[F 5]
[SUM-OF-SQUARES (add 5 1) (mul 5 2)]
[ADD (square (add 5 1))         (square (mul 5 2))]
     [SQUARE (add 5 1)]
     [MUL (add 5 1) (add 5 1)]
          [ADD 5 1]
                    [ADD 5 1]
                                [SQUARE (mul 5 2)]
                                [MUL (mul 5 2) (mul 5 2)]
                                     [MUL 5 2]
                                               [MUL 5 2]
136

Which is more or less what the book illustrates the process should be.


Writing these sorts of macros is basically like writing a normal procedure, except that

  • Instead of DEFINE, you use DEFINE-MACRO.
  • There is no implicit BEGIN over the body, so you need to supply your own if you have multiple expressions.
  • The entire body expression is prefixed with a grave accent.
  • All instances of the parameters are prefixed with a comma.

That's Writing Scheme Macros 101.

Now all in all, this is a little silly to spring macros on someone on just the first chapter of SICP. But if you say you're having an inordinate amount of difficulty modifying Racket to do what you want (and then there are those not using Racket at all), then here's an alternative.

楠木可依 2024-09-15 20:23:39

Racket 有一个 懒惰的语言。它比解释器更好,因为您可以编写由普通球拍模块和惰性模块组成的程序。

至于使用打印输出进行调试——你可以用这种惰性语言来做到这一点,并且你会得到类似于 Haskell 中的不安全 IO 的东西。但有时这仍然会令人困惑。 (如果你想要一个解释器将打印输出插入其中,它也会令人困惑,因为它遵循惰性求值......)

Racket has a lazy language. It's better than just an interpreter, since you can write programs that are made of plain racket modules and lazy ones.

As for debugging using printouts -- you can do that in this lazy language, and you get something similar to unsafe IO in Haskell. This can still be confusing sometimes though. (And if you wanted an interpreter to plug printouts into it, it would also be confusing, because it follows the lazy evaluation...)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文