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

时间:2014-02-14 01:47:58

标签: lambda scheme racket sicp

当我在SICP中遇到以下关于cons和car的“替代”定义时,我才开始觉得我对球拍和方案中lambda的使用有一个模糊的理解

(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)))

对于我的生活,我无法解析它们。

有人能解释如何以对全新手有意义的方式解析或扩展这些吗?

4 个答案:

答案 0 :(得分:22)

这是一种表示数据的有趣方式:作为函数。请注意这一点 cons的定义会返回lambda 关闭参数xy,在里面捕捉他们的价值观。还要注意返回的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?就是这样!它期待的功能。

了解上述内容后,您可以轻松了解carcdr的工作原理;让我们 剖析解释器如何一步一步评估carcdr的方式:

; 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 接收该闭包并将其传递给充当选择器的函数 第0个值,cdr充当第1个值的选择器。钥匙 这里要明白的是lambda充当了一个 closure。 这有多酷?我们只需要函数来存储和检索任意数据!

car&的嵌套作品大多数LISP中cdrdefined up to 4 deep。例如:

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

答案 1 :(得分:2)

使用combinatory表示法(隐式翻译为Scheme作为currying函数,f x y = z ==> (define f (λ (x) (λ (y) z))))应该很容易理解:

cons x y m = m x y
car z = z _K          ; _K p q = p
cdr z = z (_K _I)     ; _I x = x     _K _I p q = _I q = q

所以我们得到了

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

因此定义符合我们的期望。

在英语中,cons x y值是一个函数,表示“如果你给我一个两个参数的函数我会用我持有的两个参数来调用它。让它决定什么到那么做吧!“

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

答案 2 :(得分:2)

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

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

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

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

无论pq是什么,当应用名为z的内容时,z是接受函数作为其输入的东西,那么p中的第一个选择了1}}和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

我认为这个练习的主要观点是“计算将操作和数据结合在一起,并且将它们组合在一起的顺序无关紧要。”

答案 3 :(得分:0)

我在Go中创建了一个可以运行的版本: https://play.golang.org/p/3Hz6ss-9ghr