在SICP中使用lambda定义cons/car/cdr

在SICP中使用lambda定义cons/car/cdr,lambda,scheme,racket,sicp,Lambda,Scheme,Racket,Sicp,我刚开始觉得我对lambda在racket和scheme中的用法有一个模糊的理解,这时我在SICP中遇到了cons和car的以下“替代”定义 (define (cons x y) (lambda (m) (m x y))) (define (car z) (z (lambda (p q) p))) (define (cdr z) (z (lambda (p q) q))) 就我而言,我就是无法解析它们 有谁能解释一下如何以一种对新手都有意义的方式解析或扩展这些数据吗?这是一种

我刚开始觉得我对lambda在racket和scheme中的用法有一个模糊的理解,这时我在SICP中遇到了cons和car的以下“替代”定义

(define (cons x y)
   (lambda (m) (m x y)))

(define (car z)
  (z (lambda (p q) p)))

(define (cdr z)
  (z (lambda (p q) q)))
就我而言,我就是无法解析它们


有谁能解释一下如何以一种对新手都有意义的方式解析或扩展这些数据吗?

这是一种表示数据的有趣方式:作为函数。注意这个
cons
的定义返回一个
lambda
,该lambda在参数
x
y
,在其中捕获它们的值。还请注意,返回的lambda 接收函数
m
作为参数:

;creates a closure that "remembers' 2 values
(define (cons x y)    (lambda (m) (m x y)))
;recieves a cons holding 2 values, returning the 0th value
(define (car z)       (z (lambda (p q) p)))
;recieves a cons holding 2 values, returning the 1st value
(define (cdr z)       (z (lambda (p q) q)))
在上述代码中,
z
是一个闭包,与由
cons
创建的闭包相同,并且在 程序体我们将另一个
lambda
作为参数传递给它, 还记得
m
?就是这样!它所期望的功能

了解了以上内容,就很容易了解
car
cdr
的工作原理;让我们 剖析解释器如何一步一步地评估
car
cdr

; lets say we started with a closure `cons`, passed in to `car`
(car (cons 1 2))

; the definition of `cons` is substituted in to `(cons 1 2)` resulting in:
(car (lambda (m) (m 1 2)))

; substitute `car` with its definition
((lambda (m) (m 1 2)) (lambda (p q) p))

; replace `m` with the passed parameter
((lambda (p q) p) 1 2)

; bind 1 to `p` and 2 to `q`, return p
1
总而言之:
cons
创建一个“记住”两个值的闭包,
car
接收该闭包并将其传递给一个函数,该函数充当 第零个值和
cdr
充当第一个值的选择器。键 这里要理解的一点是,
lambda
充当 . 这有多酷?我们只需要存储和检索任意数据的函数

car
cdr
的嵌套组合在大多数LISP中。例如:

(define caddr (lambda (x) (car (cdr (cdr x)))))

这应该很容易用符号来理解(隐式转换为Scheme作为currying函数,
fxy=z=>(定义f(λ(x)(λ(y)z)))

所以我们得到了

car (cons x y) = cons x y  _K     = _K  x y   =  x
cdr (cons x y) = cons x y (_K _I) = _K _I x y = _I y = y
所以这些定义符合我们的预期,很简单

在英语中,
consxy
值是一个函数,表示“如果你给我一个包含两个参数的函数,我将用我持有的两个参数调用它。那就让它来决定怎么处理它们吧!“


换句话说,它需要一个“continuation”函数,并使用its中使用的两个参数(即“pair”)调用它创建。

在我看来,决定性的诀窍是从头到尾阅读定义,因为在这三个定义中,自由变量总是那些可以在体内的lambda中找到的变量(
m
p
q
)。以下是将代码从结尾(右下角)到开头(左上角)翻译成英语的尝试:

无论
m
是什么,我们怀疑它是一个函数,因为它正好出现在
)旁边,它必须同时应用于
x
y
:这是
cons
ing
x
y
的定义

无论
p
q
是什么,当应用名为
z
的东西,并且
z
是接受函数作为其输入的东西时,则选择
p
q
中的第一个:这是
car
的定义

例如,“接受函数作为输入的东西”,我们只需要回顾一下
cons
的定义。这意味着
car
接受
cons
作为输入

(car (cons 1 2)) ; looks indeed familiar and reassuring
(car (cons 1 (cons 2 '()))) ; is equivalent
(car '(1 2)) ; is also equivalent
(car z)
; if the previous two are equivalent, then z := '(1 2)
最后一行的意思是:列表是“接受函数作为输入的东西”

别让你的脑袋在那一刻打转!无论如何,列表只接受可以在列表元素上工作的函数。这种情况恰恰是因为我们重新定义了
cons


我认为这个练习的要点是“计算是把运算和数据结合在一起,而把它们按什么顺序结合起来并不重要。”

谢谢。我想我明白了(但这会伤到我的大脑)。这比他们描述的另一种形式要复杂得多:(define(cons x y)(define(调度m)(cond(=m0)x(=m1)y))调度(定义(车辆z)(z0))看起来我需要理解闭包-感谢对闭包的引用。另一种方法在概念上更复杂。它需要条件、比较、函数和函数应用程序-而这种方法只需要函数和函数应用程序。可能是因为我还不熟悉函数语言,第二个对我来说似乎更简单。在我看来,在“分派”替代方案中,cons生成一个潜伏在等待中的函数,以便在被很好地询问时生成正确的输出——这似乎很简单。但在“lambda”替代方案中,cons生成一个只有在“引导”时才能理解的幻影“坐车。在这两种情况下,都有一个函数潜伏着,等待着被很好地询问:)@scarLópez让我等了24小时才授予250磅的奖金,我还提交了一份编辑,添加了关于嵌套的
cdadr
s的信息,希望您批准;)您可能对接受的答案显示如何实现存储一个值的单元格感兴趣。这是同样的想法。还要看看哪个有类似于您的定义:
pair≡ λx.λy.λz.z x y
。我悬赏是为了奖励当前的答案,但由于种种原因,我不得不等待24小时。@GlassGhost:我希望这个答案对其他人有用,那么:)
(define (cons x y)
    (lambda (m) (m x y))
(define (car z)
    (z (lambda (p q) q)))
(car (cons 1 2)) ; looks indeed familiar and reassuring
(car (cons 1 (cons 2 '()))) ; is equivalent
(car '(1 2)) ; is also equivalent
(car z)
; if the previous two are equivalent, then z := '(1 2)