怎么让*定义在Chez Scheme / Racket?

时间:2014-03-27 17:53:13

标签: scheme racket let chez-scheme

Chez Scheme / Racket如何定义let*?特别是,为什么第一个例子评估为6 ...

(let* ((let +) (a (let 2 4)))
    a)

...当我对exercise 3.1.3的理解是let*可以扩展为嵌套let(甚至是嵌套的let*)语句时,可以将上面的示例扩展为人们会期望解释器在错误中做结果吗?

(let ((let +))
    (let (a (let 2 4))
        a))

实施是否与练习不同?我希望第一个例子由于let的新定义而导致错误。

3 个答案:

答案 0 :(得分:5)

(let *([let +] [a(let 2 4)])a)

变为

(LET([let +])   (LET([a(let 2 4)])     a))的

其中LET指的是定义let *的地方的“宏”(正如Chris写的那样:“卫生”)。

当评估它时,LET会将+的值绑定到let。 计算(let 2 4)的值,这是6(由于let的绑定)。 然后6绑定到a。最后评估身体,由于a与6绑定,结果为6。

答案 1 :(得分:2)

让我们假设let*的这个定义(我试图让这个变得尽可能简单,所以它不是"工业强度"作为Astau Takikawa所关联的球拍):

(define-syntax let*
  (syntax-rules ()
    ;; base case
    ((_ () body ...)
     (let ()
       body ...))

    ;; recursive case
    ((_ (binding next ...) body ...)
     (let (binding)
       (let* (next ...)
         body ...)))))

Scheme有一个名为 hygiene 的概念,它表示宏中的任何免费标识符(即宏中未定义的标识符)都将绑定到宏的值#39的定义。对于上述let*宏,免费标识符为letlet*,因为它们未在其他位置绑定(binding,{{1}在宏中,和next都是。)

这意味着在该宏中,bodylet将具有宏定义时的值以及用户代码(围绕使用宏)不会对使用的let*let的值产生影响。

实现此卫生的一种方法是通过重命名。因此,通过重命名,上面的宏可以重命名如下:

let*

此处,(define-syntax let* ;; bind g1 to current let, g2 to current let* (syntax-rules () ((_ () g3 ...) (g1 () g3 ...)) ((_ (g4 g5 ...) g6 ...) (g1 (g4) (g2 (g5 ...) g6 ...))))) g1是生成的临时符号,通常称为" gensyms" (在Lisp函数g6之后,创建了这样的东西)。请注意,由于重命名,用户代码不会影响宏中gensymlet的定义,也不会影响let*,{{1}的宏绑定}和binding不会影响可能在next正文中使用此类标识符的任何用户代码。

脚注(如果您的学生需要对此进行更深入的处理):对于许多Scheme实现,gensyms是未处理的(它们不会进入符号池,与普通符号不同,它们都是实体符号)。然后,即使用户碰巧正确地猜测"由重命名过程生成的标识符(例如,即使它们碰巧在上面的示例中使用bodylet*等),它们实际上也不会与宏的标识符冲突实际使用。

然而,标准Scheme没有讨论未加密的符号,并且在标准Scheme的上下文中,所有符号都被实现,因此它对于Scheme实现来说完全有效,即使对于gensyms,也只能使用实际符号。在这种情况下,可以通过与重命名的符号碰撞来创建破坏卫生的方法。

答案 2 :(得分:0)

来自R7RS的let*的官方定义:

(define-syntax let*
  (syntax-rules ()
    ((let* () body1 body2 ...)
     (begin body1 body2 ...))

    ((let* ((name1 val1) (name2 val2) ...)  body1 body2 ...)
     (let ((name1 val1))
       (let* ((name2 val2) ...)
         body1 body2 ...)))))

这表明let*扩展为嵌套的let表达式。您的错误产生了,因为在您使用letlet*定义了let时,Scheme卫生宏不会混淆let*绑定。