过程(C 中称为函数,其实是同一个东西)作为参数,是一种建立抽象的手段。它极大地增强了语言的表达能力。我们可以使用这种方式简化代码。
Scheme
在 Scheme 中,可以直接把过程作为参数传递给过程,就像把数据传递给过程一样。
(define (cube x) (* x x x)) ;; 定义一个过程 cube,x 是参数,计算 x 的立方
(define (add f a b) ;; 定义一个过程 add,f、a、b 是三个参数,计算 (f a)+(f b) 的值,f 代表一个过程
(+ (f a) (f b)))
接下来举一个例子,看看过程作为参数的强大之处。例子取自 SICP。
考虑下面的三个过程,第一个计算从 a 到 b 的各整数之和。例如,1 + 2 + 3 +...
(define (sum-integers a b)
(if (> a b)
0
(+ a (sum-integers (+ a 1) b)))) ;; 递归调用
第二个计算给定范围内的整数的立方之和。例如,1^3 + 2^3 + 3^3 +...
(define (sum-cubes a b)
(if (> a b)
0
(+ cube a) (sum-cubes (+ a 1) b))))
第三个计算类似下面的序列之和:1/(1*3) + 1/(5*7) + 1/(9*11) +...
(define (pi-sum a b)
(if (> a b)
0
(+ (/ 1.0 (* a (+ a 2))) (pi-sum (+ a 4) b))))
这三个过程存在着一种公共的模式,数学家称之为“求和记法”。我们可以通过过程作为参数把这种模式表示出来。
(define (sum term a next b) ;; term 和 next 是过程
(if (> a b)
0
(+ (term a) ;; 调用 term
(sum term (next a) next b)))) ;; 调用 next
利用sum
过程重新构造上面三个计算。
(define (inc n) (+ n 1))
;; 第一个计算
(define (sum-integers a b)
(define (identity x) x) ;; 定义一个内部过程
(sum identity a inc b)) ;; 过程定义。调用 sum
;; 第二个计算
(define (sum-cubes a b)
(sum cube a inc b))
;; 第三个计算
(define (pi-sum a b)
(define (pi-term x)
(/ 1.0 (* x (+ x 2))))
(define (pi-next x)
(+ x 4))
(sum pi-term a pi-next b))
一旦有了 sum
,我们就能用它做为基本构件,去形式化其他概念。
C
C 中的过程作为参数,可以用函数指针来实现。函数指针,即指向函数的指针。
int add(int a, int b) { // 定义一个函数
return a + b;
}
int sub(int a, int b) { // 参数、返回值跟 add 函数相同
return a - b;
}
// 使用函数指针
int use_func(int (*func)(int, int), int a, int b) {
return (*func)(a, b);
}
int (*func)(int, int)
是函数指针定义,它放在 use_func
函数的参数表里,表明这个参数接受一个函数名做为参数,但是这个函数名所表示的函数需要两个 int
参数,返回值为 int
。有了上述函数定义之后,我们可以在 main
函数中使用。
int main(void) {
printf("%d\n", use_func(add, 1, 2)); // 3
printf("%d\n", use_func(sub, 3, 4)); // -1
return 0;
}
有了上面的基础,接下来定义 C 中的 sum
函数。上面第一个和第二个计算返回值的数据类型是 int
,第三个计算返回值的数据类型是 double
。为了代码的简洁,这里不使用泛型,统一为 double
。
double sum(double (*term)(int), int a, int (*next)(int), int b) { // 有两个函数指针,term 和 next
if (a > b) {
return 0;
} else {
return (*term)(a) + sum(term, (*next)(a), next, b); // 调用 term 和 next
}
}
用 sum
去构造前面三个计算。
// 第一个计算
double sum_integers(int a, int b) {
return sum(identity, a, inc, b); // 调用 sum
}
// 第二个计算
double sum_cubes(int a, int b) {
return sum(cube, a, inc, b);
}
// 第三个计算
double pi_sum(int a, int b) {
return sum(pi_term, a, pi_next, b);
}
double identity(int x) {
return x;
}
double cube(int x) {
return x * x * x;
}
double pi_term(int x) {
return 1.0 / (x * (x + 2));
}
int inc(int x) {
return x + 1;
}
int pi_next(int x) {
return x + 4;
}
最后,在 main
函数中测试一下。
int main(void) {
printf("%lf\n", sum_integers(1, 10)); // 55.000000
printf("%lf\n", sum_cubes(1, 10)); // 3025.000000
printf("%lf\n", pi_sum(1, 1000) * 8); // 计算 pi 的近似值。3.139593
return 0;
}
通过 Scheme 和 C 的比较,我们可以看到 Scheme 支持在过程中定义过程,而 C 不支持在函数中定义函数。
网友评论