4

How is let* defined in Chez Scheme/Racket? In particular, why does this first example evaluate to 6...

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

...when my understanding from exercise 3.1.3 is that let* can be expanded to nested let (or even nested let*) statements, but expanding the above example as one would expect the interpreter to do results in an error?

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

Is the implementation different than in the exercise? I would expect that first example to also result in an error due to the new definition of let.

4

3 回答 3

5

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

变成

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

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

当 this 被计算时,LET 会将 + 的值绑定到 let。(let 2 4) 的值被计算出来,这是 6(由于 let 的绑定)。然后 6 绑定到 a。最后计算主体,因为 a 绑定到 6,所以结果是 6。

于 2014-03-27T18:43:33.773 回答
2

让我们假设以下定义let*(我试图使其尽可能简单,因此它不像 Asumu Takikawa 所关联的 Racket 那样具有“工业实力”):

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

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

Scheme 有一个称为卫生的概念,它表示宏中的任何自由标识符(即未在宏中定义的标识符)都将绑定到宏定义时的值。在上述let*宏的情况下,自由标识符是letand let*,因为它们没有绑定到宏中的其他地方(如bindingnextbodyare)。

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

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

(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 ...)))))

在这里,g1throughg6是生成的临时符号,通常称为“gensyms”(在gensym创建此类事物的 Lisp 函数之后)。请注意,由于重命名,用户代码不会影响宏的定义letlet*内部,也不会影响宏对 , 的绑定bindingnext并且body不会影响任何可能在let*.

脚注(如果您的学生想要对此进行更深入的处理):对于许多 Scheme 实现,gensyms 是非实习的(它们不进入符号池,不像普通符号,它们都是实习的)。然后,即使用户碰巧正确地“猜测”了重命名过程生成的标识符(例如,即使他们碰巧在上面的示例中使用g1,g2等),它们实际上也不会与宏的标识符发生冲突实际使用。

然而,标准的 Scheme 并没有谈论非驻留符号,并且在标准 Scheme 的上下文中,所有符号都是驻留的,因此对于 Scheme 实现来说,完全使用驻留符号是完全有效的,即使对于 gensym 也是如此。在这种情况下,可以通过与重命名的符号碰撞来创造破坏卫生的方法。

于 2014-03-29T18:01:55.623 回答
0

let*来自R7RS的官方定义:

(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表达式。出现您的错误是因为 Scheme hygienic 宏在定义时不会混淆let绑定与您使用.let*letlet*

于 2014-03-27T22:56:51.230 回答