Scheme 如何用“lambda”重写“let*”?
我理解如何将Scheme 如何用“lambda”重写“let*”?,scheme,lambda,Scheme,Lambda,我理解如何将(let((x v1)(y v2))e重写为((lambda(x y)e)v1 v2)。但是我不太熟悉let* 我们如何在lambda和函数应用程序方面重写(let*((xv1)(yv2)(zv3))e。比如说, (let* ((x v1) (y v2) (z v3)) e) 与 (let ((x v1)) (let ((y v2)) (let ((z v3)) e))) 这有助于您理解let*?:-) 更新:OP正在询问(
(let((x v1)(y v2))e
重写为((lambda(x y)e)v1 v2)
。但是我不太熟悉let*
我们如何在lambda和函数应用程序方面重写
(let*((xv1)(yv2)(zv3))e
。比如说,
(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))
(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))
(let ((x 42))
(let ((x 10)
(y (+ x 13)))
y))
返回55(42+13)。内部
x
的值不用于计算y
的值;它仅在let
此let
表达式的主体内生效:
(let ((x v1)
(y v2))
e)
(let* ((x v1)
(y v2)
(z v3))
e)
等效于以下lambda
应用程序,注意这里变量可以按任何顺序计算(没有严格的从左到右的顺序),并且一个变量的定义不能引用前面的变量:
((lambda (x y)
e)
v1 v2)
另一方面,此let*
表达式:
(let ((x v1)
(y v2))
e)
(let* ((x v1)
(y v2)
(z v3))
e)
可以转换为一系列嵌套的lambda
s,以确保变量的计算顺序与用于定义变量的顺序相同,并且首先定义的变量可以在所有后续定义中引用:
((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)
最后,这里有两本很棒的在线学习书籍:
让*
扩展到
这:
扩展到:
((lambda (a)
((lambda (b)
(cons a b))
(* 2 a)))
1)
这里有一个很好的方法来思考lambda在Scheme中的含义(很好,因为它既简单又准确):它既是程序中某个位置的标签,也是绑定变量的范围。在Scheme中,程序中某个位置的标签(如在其他语言中可以goto
to,或在机器语言中可以分支到)总是带有绑定变量的范围。您只能通过提供绑定到其范围内绑定的变量的值来“转到”程序中的某个位置
Scheme的let
是一种表达方式,“我想在这些变量被绑定的地方建立一个作用域,但我不想等到以后再告诉它们的值。我想在这里指定它们的值。”因此,let
只是一个宏,它生成lambda,然后在那里提供值
如果您希望其中一个变量的值是使用另一个变量的表达式,就像上面用a
表示b
一样,则必须在a
的范围内定义b
。因此使用了let*
宏,它在包含前一个变量的范围内定义每个连续变量。因为我们有一堆嵌套的作用域,所以它们是由一堆嵌套的lambda实现的
宏
下面是如何告诉Scheme如何将let*
重写为一组嵌套的lambda和函数应用程序:
(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)
在中,您可以在REPL中通过键入(expand'(let*([a1][b(*2a)])(cons a b))
来处理此问题,并查看结果。以下是我尝试此功能时得到的结果:
(let ([#:a 1]) (let ([#:b (#2%* 2 #:a)]) (#2%cons #:a #:b)))
这与((lambda(x y z)e)v1 v2 v3)有何不同?或者这是无效的?这只是第一种情况(a
let
)。它与第二种情况完全不同,因为在您刚刚发布为注释的代码中,变量可以按任何顺序计算(而不是像alet*
那样严格按从左到右的顺序),一个变量不能引用itI更新我的答案之前定义的其他变量,请重新阅读;)我也用一个不调用未定义行为的示例更新了我的答案。:-)非常感谢您的解释(和来源)!这帮了大忙!我懂了。所以,如果一个人编码:(let*((x42))(let*((x10)(y(+x13)))y),这是否也会导致23(10+13)?是的,但我鼓励你实际尝试一下。:-)对于那些对这个问题投了否决票的人来说:这是一个关于这个方案的好的、基本的问题,任何初学者都可能希望得到答案。这可能是一个家庭作业问题,但我认为我们应该明确回答这一问题,这样人们就可以很容易地在网上找到它,这是一个非常普遍的问题。如果你同意,请收回你的反对票。谢谢