11

通过计算机程序的结构和解释中的练习,我一直在慢慢地工作。第1.1.5节讨论了 applicative 与 normal-order 评估,该主题随后在正文中出现了好几次。由于解释器使用应用顺序评估,因此只需在代码中插入调试语句即可轻松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) (add 1 2)). 操作数12应在最终结果之前打印两次。

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

4

2 回答 2

7

事实证明,Scheme 实际上已经带有本质上是正常顺序的评估器。它们是您可能听说过很多的那些传说中的宏,我们可以重写 1.1.4--1.1.5 节的示例,以使用宏扩展代替过程应用相当容易:

(定义(打印。项目)
  (对于每个显示项目))

(定义宏(添加 xy)
  `(开始(打印“[添加”',x““',y“]”)
          (+ ,x ,y)))

(定义宏(mul xy)
  `(开始(打印“[MUL”',x““',y“]”)
          (* ,x ,y)))

(define-macro (square x)
  `(开始 (打印 "[SQUARE " ',x "]")
          (mul ,x ,x)))

(定义宏(平方和 xy)
  `(begin (print "[SUM-OF-SQUARES " ',x " " ',y "]")
          (添加(正方形,x)(正方形,y))))

(定义宏(fa)
  `(开始(打印“[F”',a“]”)
          (平方和 (add ,a 1) (mul ,a 2))))

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

如果您加载这些定义(需要注意的是 DEFINE-MACRO 是非标准的,但这在实践中应该不是问题,因为大多数方案都提供了它),然后(f 5)像本书一样进行评估将打印出来(当然我把它美化了)上升一点):

[F 5]
[平方和 (加 5 1) (mul 5 2)]
[加(平方(加 5 1))(平方(mul 5 2))]
     [正方形(加 5 1)]
     [MUL(加 5 1)(加 5 1)]
          [添加 5 1]
                    [添加 5 1]
                                [正方形 (mul 5 2)]
                                [MUL (mul 5 2) (mul 5 2)]
                                     [MUL 5 2]
                                               [MUL 5 2]
136

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


写这些宏基本上就像写一个普通的程序,除了

  • 代替 DEFINE,您使用 DEFINE-MACRO。
  • 正文中没有隐含的 BEGIN,因此如果您有多个表达式,则需要提供自己的 BEGIN。
  • 整个身体表情以重音为前缀。
  • 参数的所有实例都以逗号为前缀。

那是编写方案宏 101。

总而言之,仅在 SICP 的第一章中就向某人添加宏有点愚蠢。但是如果你说你在修改 Racket 来做你想做的事情时遇到了极大的困难(然后有些人根本不使用 Racket),那么这里有一个替代方案。

于 2010-07-10T04:59:45.623 回答
6

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

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

于 2010-07-08T16:49:58.303 回答