Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scheme 如何在Chez方案/球拍中定义let*?_Scheme_Racket_Let_Chez Scheme - Fatal编程技术网

Scheme 如何在Chez方案/球拍中定义let*?

Scheme 如何在Chez方案/球拍中定义let*?,scheme,racket,let,chez-scheme,Scheme,Racket,Let,Chez Scheme,如何在Chez方案/球拍中定义let*?特别是,为什么第一个示例的计算结果为6 (let* ((let +) (a (let 2 4))) a) …当我从中了解到,let*可以扩展为嵌套的let(甚至是嵌套的let*)语句时,但是按照解释器的预期扩展上述示例会导致错误吗 (let ((let +)) (let (a (let 2 4)) a)) 实施是否与练习中的不同?我希望第一个示例也会由于let的新定义而导致错误(let*([let+][a(let 2 4

如何在Chez方案/球拍中定义
let*
?特别是,为什么第一个示例的计算结果为6

(let* ((let +) (a (let 2 4)))
    a)
…当我从中了解到,
let*
可以扩展为嵌套的
let
(甚至是嵌套的
let*
)语句时,但是按照解释器的预期扩展上述示例会导致错误吗

(let ((let +))
    (let (a (let 2 4))
        a))
实施是否与练习中的不同?我希望第一个示例也会由于
let

的新定义而导致错误(let*([let+][a(let 2 4)])a)

变成

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

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

求值时,LET将+的值绑定到LET。 计算(let 2 4)的值,这是6(由于let的绑定)。
那么6与a绑定。最后对主体进行评估,由于a与6绑定,结果为6。

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
表达式。出现错误的原因是,当定义
let*
时,宏不会将
let
绑定与使用
let*
时的
let
绑定混淆。让我们假设
let*
的这一定义(我试图使其尽可能简单,因此它不是“工业实力”作为Asumu Takikawa链接的球拍):

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

这意味着在该宏中,
let
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
g6
是生成的临时符号,通常称为“gensyms”(在创建这些东西的Lisp函数
gensym
之后)。请注意,由于重命名,用户代码不会影响宏中
let
let*
的定义,并且宏的
binding
next
body
的绑定也不会影响在
let*
的主体中可能使用此类标识符的任何用户代码

脚注(如果您的学生希望对此进行更深入的讨论):对于许多方案实施,GenSym是不受关注的(它们不进入符号库,而普通符号都是实习的)。然后,即使用户碰巧正确地“猜测”了重命名过程生成的标识符(例如,即使在上面的示例中他们碰巧使用了
g1
g2
,等等),它们实际上也不会与宏实际使用的标识符发生冲突


然而,标准方案并没有讨论不相关的符号,在标准方案的上下文中,所有符号都是内部的,因此方案实现完全可以只使用内部符号,即使对于GenSym也是如此。在这种情况下,可以通过与重命名的符号冲突来创建破坏卫生的方法。

因为卫生。(如果有人不比我快,我以后会写一个更长的答案。)这是一个写得很好、很有趣的问题@ChrisJester Young我知道这是一个奇怪的例子,可能没有有用的应用程序。然而,我正在教一个年轻的学生,他第一次学习函数式编程(来自Java),并从尝试打破这种语言中找到乐趣。我不确定这个问题的答案。如果你真的想在Racket中看到
let*
的实现,你可以看看它不是很漂亮的代码,因为它必须在低级Racket中编写。@Asumutakawa哇。这就像试图阅读psyntax的实现一样令人震惊:-问题是“如何在Chez方案/球拍中定义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 ...)))))