Lisp 是否有使用正常顺序求值的方案解释器?

Lisp 是否有使用正常顺序求值的方案解释器?,lisp,scheme,sicp,Lisp,Scheme,Sicp,我一直在以自己的方式慢慢地练习。第节讨论了应用程序与正常顺序的求值,之后的文本中多次提到了该主题。由于解释器使用应用程序顺序求值,因此只需在代码中插入displaydebug语句就可以很容易地看到它是如何工作的。这将有助于我的理解,能够做同样的事情为正常秩序的评估 有人知道使用正常顺序求值而不是应用程序顺序实现的Scheme(或Lisp)解释器吗 更新: 下面是一个简短的例子,它是对SICP中给出的例子的修改。我将定义自己的add过程来打印参数,并使用书中的square过程 (define (a

我一直在以自己的方式慢慢地练习。第节讨论了应用程序与正常顺序的求值,之后的文本中多次提到了该主题。由于解释器使用应用程序顺序求值,因此只需在代码中插入
display
debug语句就可以很容易地看到它是如何工作的。这将有助于我的理解,能够做同样的事情为正常秩序的评估

有人知道使用正常顺序求值而不是应用程序顺序实现的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
应在最终结果之前打印两次

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

有一个解决方案。它比一个解释器更好,因为你可以编写由普通的racket模块和惰性模块组成的程序


至于使用打印输出进行调试——您可以在这种惰性语言中进行调试,您可以在Haskell中获得类似于不安全IO的东西。尽管如此,这有时仍然令人困惑。(如果您希望解释器将打印输出插入其中,这也会让人困惑,因为它遵循惰性求值…

结果表明,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)))) 这或多或少是本书所阐述的过程


编写这些宏基本上就像编写一个正常的过程,除了

  • 使用DEFINE-MACRO代替DEFINE
  • 主体上没有隐含的开始,因此如果有多个表达式,则需要提供自己的开始
  • 整个正文表达式的前缀带有严重的重音
  • 参数的所有实例都以逗号作为前缀
这就是编写Scheme宏101


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

注意,lazy Racket使用的是按需呼叫,而不是正常的顺序。在惰性球拍中,
(平方和(+51)(*52))
将计算
(+51)
(*52)
一次,而不是两次。所以你没有得到书中描述的行为。书中有一些术语问题。(例如,请参见维基百科中如何描述“applicative-”和“normalorder”)但是,是的,racket语言使用的是按需调用——即记忆版本。只需做一点小小的更改就可以使其不记忆结果(在一个文件中将
lazy
替换为
delay/name
)。我必须在哪里进行更改?使用
#lang lazy
运行正如我所希望的那样,但是
平方和
示例的计算结果与上面描述的@sepp2k一样。Bill:open
收集/lazy/force.rkt
,第7行对
~/code>有一个定义,它的末尾是
lazy
,将
lazy
替换为
延迟/name
。但事实上,现在我已经尝试过了,我发现还有其他问题会使它无法正常工作。一个快速的破解方法是替换
的简单定义(define(!p)(if(promise?p)(!(force p))p)定义(!p))
在第8行进行编码>。我仍然得到与以前相同的结果。我在问题正文中添加了一个简短的示例,以防您(或任何人)想要尝试,或者如果我只是想错了,请纠正我。 [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