SICP/Chapter2/Exercise-2.4
Lambda表达式语法
(lambda kw-formals body)
题目描述
用过程性表示方式重写序对的cons、car、cdr
Scheme代码
(define (cons-24 x y)
(lambda (m) (m x y)))
(define (car-24 z)
(z (lambda (p q) p)))
这段代码只有4行,但是逻辑关系并不好理解。
原因在于函数式语言的自顶向下实现方式不符合一般的逻辑习惯。
lambda以类似函数指针的方式提供了通用的接口,实现了动态绑定的功能。
拆解
(按照自底向上的顺序)
1 - procedure_1 = (lambda (p q) p)
Scheme语义:
C实现:
- 没有更深层次的嵌套,为完整函数体。
- p,q暂且定义为int类型
int GetCar(int p,int q){return p;}
2 - (define (car-24 z) (z procedure_1))
Scheme语义:
- car-24接受一个参数z
- body部分:因为(z procedure_1), 可判断z为一个函数,其参数为procedure_1
- 函数z的参数procedure_1为一个函数(见1)
C实现:
- 首先应该定义一个procedure_1的函数框架(函数实例见1)
typedef int (*procedure_1)(int,int)
- 然后根据(z procedure_1)可以写出car-24的参数z的框架,返回值继承自M:
typedef int (*ARG_Z)(procedure_1)
- car-24 的参数为z,返回值继承自z。由此可写出car-24的函数框架:
int car-24(ARG_Z z){
return z(procedure_1);
}
- 因为procedure_1的实例已经在对car-24的define中,所以此时M已经拥有了一个函数实现,可绑定。car-24因此变成:
int car-24(ARG_Z z){
return z(GetCar);
}
3 - procedure_2 = (lambda (m) (m x y))
Scheme语义:
- 参数格式——接收一个m作为参数
- 因为lambda中(m)的body部分为(m x y),可知m是一个过程,过程m需要两个参数,x和y。
C实现:
- 仅有函数框架而没有具体实现,与函数指针相似。具体实现需要将抽象实例化,因此无法确定返回值。
- 首先定义参数m的框架:
typedef unknown (*m)(x,y);
- 然后可写出procedure_2的框架:
typedef unknown (*procedure_2)(m);
- 二者返回值未知,取决于绑定类型。procedure_2的返回值继承自m
4 - (define (cons-24 x y) procedure_2)
Scheme语义:
- 参数格式为x,y
- body部分为之前分析的procedure_2,因此这个define生成的是一个过程,需要接受x y参数。
C实现:
目前内层函数未被绑定,只能给出框架。唯一确定的是返回值为procedure_2类型,即返回一个函数指针。
typedef procedure_2 (*cons-24)(x,y)
(拆解完毕)
绑定过程
实例
(define test (cons-24 1 2))
(car-24 test)
过程
1 - 生成一个名为test的cons-24实例,该实例需要一个过程作为参数
2 - car-24的参数为test,car-24内置的GetCar函数作为其参数test的参数,实现功能为test(GetCar)。
3 - 逻辑理解的难点:
- 框架和数据在cons-24中给定,却不做任何处理;而是将整个框架抽象成一个类型为过程的变量(或者说抽象成函数指针)。
- 使用时需要car-24接收此函数指针,传递自身的内置函数给这个函数指针作为参数,形成一个嵌套的环节。
- car-24仅仅是一个对内置函数封装,为内置函数加上一层壳
- 两部分间的正常使用需要一个默认的规则:即car-24的参数必须为cons-24类型的过程。这在逻辑上并没有体现。
- 如果从C的角度来看,函数间的层次关系并不清晰,相互依赖。
- 但是仅从抽象接口的目的来看,提供了一个相当好的过程接口。若以C函数实现那么需要三层嵌套的定义。由此可见函数式语言与面向过程语言的不同之处。