SICP 习题 1.29 要求辛普森规则求函数f在范围a 和 b之间的定积分的近似值。
在经过前面习题的磨练之后,我对这种充满数学定义的题目已经麻木了,觉得自己能完成题目就行,有些时候不需要去理会哪些折磨人的数学定义,比如什么函数的定积分,更不用说什么辛普森规则。
其实SICP在1.3.1这节主要讲的是将过程作为参数,这里的题目也是看你是否能掌握这个概念,可以轻松地将一个过程作为参数进行传递和使用。
说实话,对于很多程序员来讲这是个挑战,之前都是将数据作为参数进行传递,没试过将一个过程,或者说一个函数,当做参数进行传递的。有些时候需要实现这种功能都是使用一些模式,比如命令模式等,将一个函数加入到一个类中,然后将该类的实例对象作为参数进行传递,由参数的接受者根据对象的识别并对函数进行调用。
在LISP中,程序员需要突破这个限制,把过程和数据当同样的“一等公民”来对待,不要对过程有歧视。
其实,在之前的练习中1.4中,作者已经向我们呈现了过程作为返回值的这种使用方式,只是没有专门介绍如何将过程当做参数和返回值使用,更是没有介绍这种使用方式对程序结构带来的巨大变化。
后面就要开始专门介绍如何将过程当做“一等公民”对待了,其实,只要把握了这个关键概念,后面的习题都不是很难。
回到习题1.29, 我们要做的就是实现下面这个函数:
x= (h/3) * (y(0) + 4*y(1)+ 2*y(2)+4*(y3)+2*(y4)+…… 2*y(n-2) + 4*y(n-1)+ y(n))
其中h=(b-a)/n
而y(k)=f(a+k*h),就是说y(k)=f(a+ k* (b-a)/n)
关键就是这里的f是什么,习题中并没有说明f是什么,却要求我们去求f的定积分。
这里就包含了高阶函数的关键,在支持高阶函数的环境里,我们可以将一个函数当做参数传递给另一个函数,另一个函数在不知晓传入函数的细节的情况下直接对传入函数进行调用。
具体到本题,我们知道有个叫f的函数会传进来,我们不需要了解f函数具体干什么,我们可以直接通过(f x)调用函数f。
就是说我们需要定义一个过程(Simpson f a b n)
然后返回(h/3) * (y(0) + 4*y(1)+ 2*y(2)+4*(y3)+2*(y4)+…… 2*y(n-2) + 4*y(n-1)+ y(n))
观察上面的计算公式,可以发现核心是下面这部分:
4*y(1)+ 2*y(2)+4*(y3)+2*(y4)+…… 2*y(n-2) + 4*y(n-1)
而且规律很明显,就是
4*y(奇数)+2*y(偶数)
如果把上面这部分称之为“核心部分”的话,就可以将整个公式转换为:
(h/3) * (y(0) + 核心部分+ y(n))
核心部分根据它的规律,定义的方法如下:
(define (All-other-y f a b n h k)
(if (even? k)
(+ (* 2 (f (+ a (* k h)))) (all-other-y f a b n h (+ k 1)))
(+ (* 4 (f (+ a (* k h)))) (all-other-y f a b n h (+ k 1)))))
主要是利用了递归,不断计算y(k),并进行累加。
不过上面的方法是不会结束的,一直递归进去,如果考虑到y(n)那部分,可以对k进行判断,如果k=n,则,直接返回y(n),结果如下:
(define (All-other-y f a b n h k)
(if (= k n)
(f (+ a (* k h)))
(if (even? k)
(+ (* 2 (f (+ a (* k h)))) (all-other-y f a b n h (+ k 1)))
(+ (* 4 (f (+ a (* k h)))) (all-other-y f a b n h (+ k 1))))))
可以发现,以上部分是没有计算y(0)部分的,如果加上y(0)的话,过程如下:
(define (All-y f a b n h)
(+ (f a) (All-other-y f a b n h 1)))
最后通过一个叫Simpson的过程将以上部分包装起来,先判断n是不是偶数,如果不是的话就报错,是的话就调用(all-y)过程,再乘以(b-a)/n/3。
最终结果如下:
(define (Simpson f a b n)
(if (not (even? n))
"Error: n shoule be even"
(* (/ (/ (- b a) n) 3) (All-y f a b n (/ (- b a) n)))))
解这道题对于数学比较精通的同学来讲比较容易,他们比较熟悉定积分的概念。
对于数学比较不熟的同学则要有勇气跳过数学部分,从更抽象的层面去理解这道题的题干,最终做到在不理解定积分的情况下也可以很好地完成练习。