我了解如何将(let ((x v1) (y v2)) e)
重写为((lambda (x y) e) v1 v2)
。但我不太熟悉let*
。
我们如何根据lambda和函数应用程序重写(let* ((x v1) (y v2) (z v3)) e)
?
答案 0 :(得分:4)
此let
表达式:
(let ((x v1)
(y v2))
e)
等同于以下lambda
应用程序,注意到在这里可以按任何顺序评估变量(不强制执行严格的从左到右的顺序),并且一个变量的定义不能引用变量在它之前:
((lambda (x y)
e)
v1 v2)
另一方面,这个let*
表达式:
(let* ((x v1)
(y v2)
(z v3))
e)
可以转换为一系列嵌套lambda
,以确保变量按照用于定义它们的相同顺序进行求值,并且首先定义的变量可以在所有后续引用中引用定义:
((lambda (x)
((lambda (y)
((lambda (z)
e)
v3))
v2))
v1)
另一个例子:只有在我们使用第二个转换时,此代码才有效:
(let* ((x 1)
(y (+ x 1)))
(+ x y))
正如您所看到的,y
引用x
的定义只会以这种方式运作:
((lambda (x)
((lambda (y)
(+ x y))
(+ x 1)))
1)
最后,这里有两本很棒的在线学习计划书:
答案 1 :(得分:1)
let*
只是嵌套的let
个实例。例如,
(let* ((x v1)
(y v2)
(z v3))
e)
与
相同(let ((x v1))
(let ((y v2))
(let ((z v3))
e)))
这对您理解let*
有帮助吗? : - )
更新:OP正在询问(在评论Óscar的帖子中)let*
与let
的区别。这是一个例子:首先,让我们使用let*
:
(let ((x 42))
(let* ((x 10)
(y (+ x 13)))
y))
返回23(10 + 13)。使用内部x
的值,并且外部x
的值被遮蔽。
现在,让我们来看看如果我们使用let
代替let*
会发生什么:
(let ((x 42))
(let ((x 10)
(y (+ x 13)))
y))
返回55(42 + 13)。内部x
的值不用于计算y
的值;它只在let
的主体内生效。
答案 2 :(得分:1)
let*
扩展为此:
(let* ([a 1] [b (* 2 a)])
(cons a b))
扩展到:
((lambda (a)
((lambda (b)
(cons a b))
(* 2 a)))
1)
这是一种很好的方式来考虑lambda
在Scheme中的含义(很好,因为它既简单又准确):它既是程序中的位置的标签,也是的范围绑定变量。在Scheme中,程序中某个位置的标签(就像你可以goto
在其他语言中使用或用机器语言分支)总是带有绑定变量的范围。您只能通过提供绑定到其范围内绑定的变量的值来“转到”程序中的某个位置。
Scheme的let
是一种说法,“我想创建一个绑定这些变量的范围,但我不想等到以后告诉它们的值。我想指定它们的值是正确的这里。”因此,let
只是一个生成lambda的宏,然后在那里提供值。
如果您希望其中一个变量的值是使用另一个变量的表达式,例如b
以a
表示的方式,那么b
必须在a
的范围内定义。因此let*
宏定义了包含前一个变量的范围中的每个连续变量。由于我们有一堆嵌套的作用域,它们是由一堆嵌套的lambdas实现的。
以下是如何告诉Scheme如何将let*
重写为一堆嵌套的lambdas和函数应用程序:
(define-syntax let*
(syntax-rules ()
[(__ () body ...)
(begin body ...)]
[(__ ([v e] [v* e*] ...) body ...)
((lambda (v)
(let* ([v* e*] ...)
body ...))
e)]))
(let* ([a 1] [b (* 2 a)])
(cons a b))
=> (1 . 2)
在Chez Scheme中,您可以在REPL中输入(expand '(let* ([a 1] [b (* 2 a)]) (cons a b))
并查看结果。这是我尝试时出现的内容:
(let ([#:a 1]) (let ([#:b (#2%* 2 #:a)]) (#2%cons #:a #:b)))