当我在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)))
对于我的生活,我无法解析它们。
有人能解释如何以对全新手有意义的方式解析或扩展这些吗?
答案 0 :(得分:22)
这是一种表示数据的有趣方式:作为函数。请注意这一点
cons
的定义会返回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
接收该闭包并将其传递给充当选择器的函数
第0个值,cdr
充当第1个值的选择器。钥匙
这里要明白的是lambda
充当了一个
closure。
这有多酷?我们只需要函数来存储和检索任意数据!
car
&的嵌套作品大多数LISP中cdr
为defined 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中找到的那些(m
,p
和q
)。这是尝试将代码翻译成英语,从结尾(右下角)到开头(左上角):
(define (cons x y)
(lambda (m) (m x y))
无论
m
是什么,我们怀疑它是一个函数,因为它出现在(
旁边,它必须同时应用于x
和y
:是cons
ingx
和y
的定义。
(define (car z)
(z (lambda (p q) q)))
无论
p
和q
是什么,当应用名为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