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
- 主体上没有隐含的开始,因此如果有多个表达式,则需要提供自己的开始
- 整个正文表达式的前缀带有严重的重音
- 参数的所有实例都以逗号作为前缀
总而言之,在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