对 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 存在于某个地方,在&lt;expr2&gt; 内部,所以它更像是:

(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&lt;expr1&gt;&lt;expr2&gt;,那么转换公式应该就清楚了:

【讨论】:

x 不需要在任何地方使用 - (let ((x 0)) 99)((lambda (x) 99) 0) 相同。 对我来说,恰恰相反:(x =&gt; ...x...) val "means" let x = val in ...x... 这是不言自明的("open up a fresh environment frame;在其中设置一个名为x 的地方;将val 的值放在那个地方;继续...")。 示例应该使用更有意义的名称。你会不会对此感到困惑:((lambda (param) body ...) arg) &lt;--&gt; (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)) &lt;body&gt;) 时,您会创建一个框架,其中x&lt;body&gt; 中可用,并为其分配值42

嗯,还有另一个工具可以让您打开新框架。事实上,它通常是您可以用来构建更多抽象结构的基本砖块:它被称为lambda

lambda 打开一个新框架,其中的参数可用于其主体。 当您编写(lambda (x) &lt;body&gt;) 时,您使x 可用于函数的&lt;body&gt;

lambdalet 之间的唯一区别是 let 立即将值分配给 x,而 lambda 将值作为参数等待。

因此,如果您想用lambda 直接分配值包装&lt;body&gt;,您只需传递该值!

((lambda (x) <body>) 42)

这使得它完全等同于:

(let ((x 42)) <body>)

【讨论】:

以上是关于对 let 到 lambda 转换感到困惑的主要内容,如果未能解决你的问题,请参考以下文章

对 Clojure 中的“let”感到困惑

对flatMap / Map转换的理解感到困惑

方案:从 Let 转换为 Lambda

对如何在 ruby​​ 中使用 lambda 进行选择感到困惑

let 中的球拍创建函数/lambda

对在 django 中将 CSV 文件转换为 ZIP 文件感到困惑