let in scheme的语法
Syntax of let in scheme
我无法理解 let
在我找到的示例中的用法。我正在使用鸡肉方案。
(let loop ()
(print "hello world")
(loop)
)
就是一个简单的死循环,递归调用自己,语法我看不懂。我知道第一个参数必须是对 ((<var[1]> <value[1]>)...(<var[n]> <value[n]))
的列表,其他参数是 let 的主体。
那么,为什么这段代码有效?
这是一个 named let
,它是 shorthand 辅助过程,通常用于使用递归进行循环,参数随着递归的进行而增加(尽管在您的代码中没有使用参数) .例如这个程序:
(define (test)
(let loop ((i 5))
(cond ((<= i 0) 'ok)
(else (print i)
(loop (- i 1))))))
...相当于这个:
(define (test)
(define (loop i)
(cond ((<= i 0) 'ok)
(else (print i)
(loop (- i 1)))))
(loop 5))
现在你看到问题中的代码片段和写这个是一样的:
(define (loop)
(print "hello world")
(loop))
(loop)
另请注意,"loop"这个名字只是约定俗成,您不妨将其命名为"iter"或"helper"或您喜欢的任何其他名称,这并不重要.
之所以有效,是因为您关于第一个 "argument" 需要成为绑定列表的说法是错误的。 let
的语法是这样的:
(let name ((binding expression) ...)
body ...)
名称是可选的。您可以有零个或多个绑定。宏如何看待它是否被命名是因为绑定需要是一个列表,而名称绝对需要是一个标识符。没有名称,它与:
((lambda (binding ...)
body ...)
expression ...)
然而名字变成了:
((letrec ((name (lambda (binding ...)
body ...)))
name)
expression ...)
当然 letrec
是根据 let
:
定义的
(letrec ((name expression) ...)
body ...)
; ===
(let ((name 'undefined) ...)
(let ((tmp expression) ...)
(set! name tmp) ...)
body ...)
从而使上面的命名 let 变成这样:
(((lambda (name)
((lambda (tmp) (set! name tmp)) (lambda (binding ...) body ...))
name)
'undefined)
expression ...)
注意名称未绑定在首次调用的框架上的技巧。它被返回并立即被调用。因此,在这些表达式中,您实际上可以从自由变量中计算 name
而没有命名的 let
干扰:
(let ((name "sylwester"))
(let name ((cur (list name))
(n 2))
(if (zero? n)
cur
(name (append cur cur) (- n 1)))))
; ==> ("sylwester" "sylwester" "sylwester" "sylwester")
使用 define
阴影绑定:
(let ((name "sylwester"))
(define (name cur n)
(if (zero? n)
cur
(name (append cur cur) (- n 1))))
(name (list name) 2))
; ==> (#<proc> #<proc> #<proc> #<proc>)
在R6RS report the syntax for let
does not demonstrate the more complex named let
but adds a reference to its description elsewhere in the report。这可能是因为当您只需要本地绑定时,named let
可能会让人不知所措和困惑。这个和那个 define
是顶层的两个不同的东西,而不是 Scheme 中最令人困惑的部分,所以如果它们实际上有不同的名称而不是记录在不同的地方,那么对于初学者来说这门语言可能会更容易掌握。
我无法理解 let
在我找到的示例中的用法。我正在使用鸡肉方案。
(let loop ()
(print "hello world")
(loop)
)
就是一个简单的死循环,递归调用自己,语法我看不懂。我知道第一个参数必须是对 ((<var[1]> <value[1]>)...(<var[n]> <value[n]))
的列表,其他参数是 let 的主体。
那么,为什么这段代码有效?
这是一个 named let
,它是 shorthand 辅助过程,通常用于使用递归进行循环,参数随着递归的进行而增加(尽管在您的代码中没有使用参数) .例如这个程序:
(define (test)
(let loop ((i 5))
(cond ((<= i 0) 'ok)
(else (print i)
(loop (- i 1))))))
...相当于这个:
(define (test)
(define (loop i)
(cond ((<= i 0) 'ok)
(else (print i)
(loop (- i 1)))))
(loop 5))
现在你看到问题中的代码片段和写这个是一样的:
(define (loop)
(print "hello world")
(loop))
(loop)
另请注意,"loop"这个名字只是约定俗成,您不妨将其命名为"iter"或"helper"或您喜欢的任何其他名称,这并不重要.
之所以有效,是因为您关于第一个 "argument" 需要成为绑定列表的说法是错误的。 let
的语法是这样的:
(let name ((binding expression) ...)
body ...)
名称是可选的。您可以有零个或多个绑定。宏如何看待它是否被命名是因为绑定需要是一个列表,而名称绝对需要是一个标识符。没有名称,它与:
((lambda (binding ...)
body ...)
expression ...)
然而名字变成了:
((letrec ((name (lambda (binding ...)
body ...)))
name)
expression ...)
当然 letrec
是根据 let
:
(letrec ((name expression) ...)
body ...)
; ===
(let ((name 'undefined) ...)
(let ((tmp expression) ...)
(set! name tmp) ...)
body ...)
从而使上面的命名 let 变成这样:
(((lambda (name)
((lambda (tmp) (set! name tmp)) (lambda (binding ...) body ...))
name)
'undefined)
expression ...)
注意名称未绑定在首次调用的框架上的技巧。它被返回并立即被调用。因此,在这些表达式中,您实际上可以从自由变量中计算 name
而没有命名的 let
干扰:
(let ((name "sylwester"))
(let name ((cur (list name))
(n 2))
(if (zero? n)
cur
(name (append cur cur) (- n 1)))))
; ==> ("sylwester" "sylwester" "sylwester" "sylwester")
使用 define
阴影绑定:
(let ((name "sylwester"))
(define (name cur n)
(if (zero? n)
cur
(name (append cur cur) (- n 1))))
(name (list name) 2))
; ==> (#<proc> #<proc> #<proc> #<proc>)
在R6RS report the syntax for let
does not demonstrate the more complex named let
but adds a reference to its description elsewhere in the report。这可能是因为当您只需要本地绑定时,named let
可能会让人不知所措和困惑。这个和那个 define
是顶层的两个不同的东西,而不是 Scheme 中最令人困惑的部分,所以如果它们实际上有不同的名称而不是记录在不同的地方,那么对于初学者来说这门语言可能会更容易掌握。