对 let 到 lambda 转换感到困惑
Posted
技术标签:
【中文标题】对 let 到 lambda 转换感到困惑【英文标题】:Confused by let to lambda conversion 【发布时间】:2018-08-30 10:32:50 【问题描述】:我目前正在通过Mike Vanier 浏览这个great article on Y-combinator。沿着解释删除以下行:
事实证明,任何 let 表达式都可以使用以下等式转换为等效的 lambda 表达式:
(let ((x <expr1>)) <expr2>) ==> ((lambda (x) <expr2>) <expr1>)
文章通过转换来说明这个陈述:
(define (part-factorial self)
(let ((f (self self)))
(lambda (n)
(if (= n 0)
1
(* n (f (- n 1)))))))
到:
(define (part-factorial self)
((lambda (f)
(lambda (n)
(if (= n 0)
1
(* n (f (- n 1))))))
(self self)))
现在,我明白了上面的两个代码 sn-ps 如何以及为什么相同,但我无法理解将let
转换为lambda
的一般公式是:
(let ((x <expr1>)) <expr2>)
==> ((lambda (x) <expr2>) <expr1>)
我会很感激详细的解释。
【问题讨论】:
Related. re:这篇文章的题词,显然是约翰·冯·诺依曼说的,“在数学中你不理解事物。你只是习惯了它们”。 【参考方案1】:我最终自己弄清楚了 =)。
我缺少的一点是:
(let ((x <expr1>)) <expr2>)
==> ((lambda (x) <expr2>) <expr1>)
x
存在于某个地方,在<expr2>
内部,所以它更像是:
(let ((x <expr1>)) <expr-containing-x>)
==> ((lambda (x) <expr-containing-x>) <expr1>)
话虽如此,如果我们将x
替换为f
:
(define (part-factorial self)
(let ((f (self self)))
(lambda (n)
(if (= n 0)
1
(* n (f (- n 1)))))))
以及在:
(define (part-factorial self)
((lambda (f)
(lambda (n)
(if (= n 0)
1
(* n (f (- n 1))))))
(self self)))
并且会用不同的颜色高亮x
、<expr1>
和<expr2>
,那么转换公式应该就清楚了:
【讨论】:
x
不需要在任何地方使用 - (let ((x 0)) 99)
与 ((lambda (x) 99) 0)
相同。
对我来说,恰恰相反:(x => ...x...) val
"means" let x = val in ...x...
这是不言自明的("open up a fresh environment frame;在其中设置一个名为x
的地方;将val
的值放在那个地方;继续...")。
示例应该使用更有意义的名称。你会不会对此感到困惑:((lambda (param) body ...) arg) <--> (let ((param arg)) body ...)
?【参考方案2】:
你应该想象有一个非常小的 lisp 语言,它有 lambda
但没有 let
。你想做的事:
(let ((nsq (square n)))
(+ nsq nsq))
你知道nsq
是一个新变量,let
的主体可以做成一个函数:
(lambda (nsq) (+ nsq nsq))
那么你需要使用它来获得相同的值:
((lambda (nsq) (+ nsq nsq)) (square n))
假设您的简单方案具有宏,因此您实现为let
:
(define-syntax let
(syntax-rules ()
[(let ((binding value) ...)
body ...)
((lambda (binding ...)
body ...)
value ...)]))
请注意,在许多实现中,这实际上正是以这种方式发生的。
【讨论】:
【参考方案3】:let
让您可以打开一个新环境,在其中可以使用变量。在编程语言术语中,我们说它“打开了一个新框架”。
当您编写(let ((x 42)) <body>)
时,您会创建一个框架,其中x
在<body>
中可用,并为其分配值42
。
嗯,还有另一个工具可以让您打开新框架。事实上,它通常是您可以用来构建更多抽象结构的基本砖块:它被称为lambda
。
lambda
打开一个新框架,其中的参数可用于其主体。
当您编写(lambda (x) <body>)
时,您使x
可用于函数的<body>
。
lambda
和 let
之间的唯一区别是 let
立即将值分配给 x
,而 lambda
将值作为参数等待。
因此,如果您想用lambda
直接分配值包装<body>
,您只需传递该值!
((lambda (x) <body>) 42)
这使得它完全等同于:
(let ((x 42)) <body>)
【讨论】:
以上是关于对 let 到 lambda 转换感到困惑的主要内容,如果未能解决你的问题,请参考以下文章