1.3 用高阶函数做抽象
1.3.1 过程作为参数
1.高阶过程
以过程作为参数,或者以过程作为返回值,这类能操作过程的过程称之为高阶过程。
2.应用于积分
模式一:
(define (sum term a next b)
(if (> a b)
0
(+ (term a)
(sum term (next a) next b))))
计算:其中,f = x³ ,a=0;b=1
(define (integral1 f a b dx)
(define (add-dx x) (+ x dx))
(* (sum f (+ a (/ dx 2.0)) add-dx b)
dx))
(define (cube x) (* x x x))
(integral1 cube 0 1 0.01)
>0.24998750000000042 ;;cube在0和1间积分的精确值是1/4
此方法计算积分并不准确,是一个近似值。练习1.29会给出一个更精确的求积分的方法:辛普森规则。
这种函数不能应用于其他语言,1.3.2节说明如和摆脱这种定义
1.3.2 用lambda构造过程
1. 一般形式
(lambda (<formal-parameters>) <body>)
<formal-parameters>为形式参数;<body>为过程体
lambda用于define同样的方式创建过程,此过程不与环境中任何名字相关联。
按如下方式阅读lambda表达式:
(lambda (x) ( + x 4))
该过程 以x为参数 它加起 x 和 4
2.lambda的应用
1.不需要定义辅助过程
(define (pi-sum a b)
(sum (lambda (x) (/ 1.0 (* x (+ x 2)))) ;;sum的定义是上面的模式一
a
(lambda (x) (+ x 4))
b))
2.可用作组合式的运算符
((lambda (x y z) (+ x y (* z z))) 1 2 3)
3.用let创建局部变量
1. 一般形式
(let ((<var1> <exp1>)
(<var2> <exp2>)
:
(<varn> <expn>))
<body>)
可以将它读作
令 <var1> 具有值 <exp1> 而且
<var2> 具有值 <exp2> 而且
:
<varn> 具有值 <expn>
:
在 <body>中
2.let表达式被解释为替代如下表达式的另一种语法形式
(对比 lambda可用作组合式的运算符 容易懂)
((lambda (<var1> ...<varn>)
<body>)
<exp1>
:
<expn>)
let表达式只是作为基础的lambda表达式的语法外衣罢了
1. 用于与注意
1).let使人能在尽可能接近其使用的地方创建局部变量约束:
(define (test x)
(+ x (let ((x 6)) (- x 2))))
(test 6)
>10
2).变量的值是在let之外计算的
(define (test x)
(let ((x 3)
(y (+ x 2)))
(* x y)))
(test 2)
>12
1.3.3 过程作为一般性方法
目的: 如何通过过程去直接描述这些方法
1.通过区间折半寻找方程的根
(define (average x y) (/ (+ x y) 2)) ;求两个数平均值
(define (close-enough? x y) ;;两数之差的绝对值是否足够接近0.001
(< (abs (- x y)) 0.001))
#|检测过程|#
(define (search f neg-point pos-point)
(let ((midpoint (average neg-point pos-point)))
(if (close-enough? neg-point pos-point)
midpoint
(let ((test-value (f midpoint)))
(cond ((positive? test-value)
(search f neg-point midpoint))
((negative? test-value)
(search f midpoint pos-point))
(else midpoint))))))
#|实现体:给一个函数f 以及零点所在区间的上下限|#
(define (half-interval-method f a b)
(let ((a-value (f a))
(b-value (f b)))
(cond ((and (negative? a-value) (positive? b-value))
(search f a b))
((and (negative? b-value) (positive? a-value))
(search f b a))
(else
(error "值不相符的" a b))))) ;;;如果不符合上面要求就会报错
(half-interval-method sin 2.0 4.0)
>3.1412353515625
2.找出函数的不动点
不动点: 假如函数上有一点(x y)满足函数f(x)=x 即:x=y,则称此点为该函数的不动点
找不动点的思路: 对于某个函数,通过从某个初始猜测值出发,反复的应用f 即:f(f(f(f(x))))。直到值的变化不大时,就找到了不动点。
(define tolerance 0.00001) ;;;赋值
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) tolerance))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
(fixed-point cos 1.0)
3. 开根号(平均阻尼技术)
(define tolerance 0.00001)
(define (average x y) (/ (+ x y) 2))
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) tolerance))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
(define (sqrt1 x)
(fixed-point (lambda (y) (average y (/ x y)))
1.0))
(sqrt1 4)
>2.000000000000002
这个过程可以看作求y |-> x/y 的不动点
假如要求根号x,.那我如何让你的猜测值y变换成另一个值使变换后的值接近正确答案,而且可以反复进行。这样 :y = (1/2)(y + x/y) 。运用不动点相加再平均来逼近收敛的方法叫平均阻尼技术
1.3.4 过程作为返回值
1.过程作为返回值
(define (square x) (* x x))
(define (average-damp f)
(lambda (x) (average x (f x))))
((average-damp square) 10)
>55
这里的返回过程用到了上面我们介绍lambda的第二个性质:可用作组合式的运算符
那求平方根可写:
(define (sqrt2 x)
(fixed-point (average-damp (lambda (y) (/ x y)))
1.0))
(sqrt2 4)
那求立方根可写:
(define (cube1 x)
(fixed-point (average-damp (lambda (y) (/ x (square y))))
1.0))
2. 求导:
将y = x³ 求导为 y = 3x² 。这个过程如何实现?
实现:
f`(x) = (g(x + dx) - g(x)) / dx
(define dx 0.000001)
(define (deriv g)
(lambda (x)
(/ (- (g (+ x dx)) (g x))
dx)))
3. 牛顿法(逼近)
优点: 牛顿法的收敛速度比折半法的收敛速度快得多
公式: f(x) = x - g(x)/g`(x)
证明:
设x是发f(x )=0的根,选取x0作为x的初始近似值,过点(x0 , f(x0))做曲线y = f(x) 的切线T,
T的方程为:y=f(x0) + f’(x0)(x-x0)
求出T与x轴的交点的横坐标 :x1 = x0 - f(x0)/f’(x0)
,过(x1, f(x1))做曲线y=f(x)的切线,
并求出该切线与x轴焦点的横坐标:x2 = x1 - f(x1)/f’(x1)
称x2为x* 的第二次近似值
重复上次的操作会得到x* 的近似序列。
求平方根:
(define (square x) (* x x))
(define (deriv g)
(lambda (x)
(/ (- (g (+ x dx)) (g x))
dx)))
(define (newton-transform g)
(lambda (x)
(- x (/ (g x) ((deriv g) x)))))
(define (newtons-method g guess)
(fixed-point (newton-tansform g) guess))
(define (sqrt3 x)
(newtons-method (lambda (y) (- (square y) x)) ;;找y = y² - x的不动点
1.0))
(sqrt 16)
>4
练习:
练习 1.29
解:
#|求以a为下限,以b为上限,函数f的积分(很精确)|#
(define (sum2 h f a b n k)
(if (> k n)
0
(+ (* (/ (h a b n) 3)
(cond ((= k 0) (f a))
((= k n) (f (+ a (* k (h a b k)))))
((= (remainder k 2) 0) (* 2 (f (+ a (* k (h a b n))))))
(else (* 4 (f (+ a (* k (h a b n))))))))
(sum2 h f a b n (+ k 1)))))
(define (cube x) (* x x x))
(define (integral2 a b f)
(define (h a b n) (/ (- b a) n))
(sum2 h f a b 1000 0))
(integral2 0 1 cube)
练习 1.30
解:
(define (next a) (+ a 1))
(define (term a) (a))
(define (sum term a next b)
(define (iter a result)
(if (> a b)
result
(iter (next a) (+ (term a) result))))
(iter a 0))
(define (cube a) (* a a a))
(sum cube 1 next 10)
练习 1.31
解:
递归:
(define (product f a next b)
(if (> a b)
1.0
(* (f a)
(product f (next a) next b))))
(define (factorial a b f)
(define (next a) (+ a 1))
(* (product f a next b) 4))
(define (f x)
(cond ((= (remainder x 2) 0)
(/ (+ x 2) (+ x 1)))
(else
(/ (+ x 1) (+ x 2)))))
(factorial 1 10000 f)
>3.1417497057379635
迭代:
(define (product f a next b)
(define (iter a result)
(if (> a b)
result
(iter (next a) (* result (f a)))))
(iter a 1.0))
(define (factorial a b f)
(define (next a) (+ a 1))
(* (product f a next b) 4))
(define (f x)
(cond ((= (remainder x 2) 0)
(/ (+ x 2) (+ x 1)))
(else
(/ (+ x 1) (+ x 2)))))
(factorial 1 10000 f)
>3.1417497057380084
练习 1.32
解:
递归:
product:
(define (accumulate combiner null-value term a next b)
(if (> a b)
null-value
(combiner (term a)
(accumulate combiner null-value term (next a) next b))))
(define (factorial a b f)
(define (next a) (+ a 1))
(define (combiner x y) (* x y))
(* (accumulate combiner 1.0 f a next b) 4))
sum:
(define (accumulate combiner null-value term a next b)
(if (> a b)
null-value
(combiner (term a)
(accumulate combiner null-value term (next a) next b))))
(define (sum term a next b)
(define (next a) (+ a 1))
(define (combiner x y) (+ x y))
(accumulate combiner 0 term a next b))
迭代
product:
(define (accumulate combiner null-value term a next b)
(define (iter a result)
(if (> a b)
result
(iter (next a) (combiner result (term a)))))
(iter a null-value))
(define (factorial a b f)
(define (next a) (+ a 1))
(define (combiner x y) (* x y))
(* (accumulate combiner 1.0 f a next b) 4))
sum
(define (accumulate combiner null-value term a next b)
(define (iter a result)
(if (> a b)
result
(iter (next a) (combiner result (term a)))))
(iter a null-value))
(define (sum term a next b)
(define (next a) (+ a 1))
(define (combiner x y) (+ x y))
(accumulate combiner 0 term a next b))
练习 1.33
解:
a).
(define (filtered-accumulate filter combiner null-value term a next b)
(if (> a b)
null-value
(combiner (if (filter a)
(term a)
null-value)
(filtered-accumulate filter combiner null-value term (next a) next b))))
(define (divisor a b f)
(define (next a) (+ a 1))
(define (combiner x y) (+ x y))
(define (f x) x)
(filtered-accumulate prime? combiner 0 f a next b)
)
b).
(define (filtered-accumulate filter combiner null-value term a next b)
(if (> a b)
null-value
(combiner (if (filter a)
(term a)
null-value)
(filtered-accumulate filter combiner null-value term (next a) next b))))
(define (divisor n f)
(define (next a) (+ a 1))
(define (combiner x y) (* x y)) ;;;求乘积
(define (f x) x) ;;;为了列出a到b之间每一个数
(define (filter a) (= (GCD a n) 1)) ;;;此处的filter过滤出与n互素的正整数
(filtered-accumulate filter combiner 0 f 0 next n)
练习 1.34
解:
(define (f g)
(g 2))
(define (square x) (* x x))
(f square)
(f (lambda (z) (* z (+ z 1))))
(f f)
报错:
application: not a procedure;
expected a procedure that can be applied to arguments
given: 2
arguments...
会报错,因为f过程需要一个过程作为参数,当用f本身作为这个参数时;f过程的内部运行(g 2) 变成了
(f 2),而f过程需要的时过程参数不是常数,所以会报错。
练习 1.35
解:
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) 0.0001))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
(define (huang)
(fixed-point (lambda (x) (+ (/ 1 x) 1)) 1.0))
(huang)
练习 1.36
解:
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) 0.0001))
(define (try guess)
(display guess) ;;;打印出guess的值
(let ((next (f guess)))
(newline) ;;;换行
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
(define (han)
(fixed-point (lambda (x)(/ (log 1000) (log x))) 1.1))
(han)
练习 1.37
解:
方法一:
(define (cont-frac n d k)
(define (iter i result)
(if (> i k)
result
(iter (+ i 1) (/ (n i) (+ (d i) result)))))
(iter 1 1.0))
(cont-frac (lambda (i) 1.0) ;;;这里用lambda定义的过程由于是用参数传入,在上面定义cont-frac的时候就相当有了个名字所以使用的时候写成(n i)
(lambda (i) 1.0)
11)
方法二(练习1.38建议使用版):
(define (cont-frac n d k)
(define (iter i null-value)
(if (> i k)
null-value
(/ (n i) (+ (d i) (iter (+ i 1) null-value)))))
(iter 1 1.0))
(cont-frac (lambda (i) 1.0)
(lambda (i) 1.0)
11)
当k=11时能保证得到的近似值具有十进制的4位精度。
练习 1.38
解:
(define (cont-frac n d k)
(define (iter i null-value)
(if (> i k)
null-value
(/ (n i) (+ (d i) (iter (+ i 1) null-value)))))
(+ (iter 1 1.0) 2))
(cont-frac (lambda (i) 1.0)
(lambda (i) (cond ((= (remainder (+ i 1) 3) 0) (* 2 (/ (+ i 1) 3)))
(else 1)))
1000)
练习 1.39
解:
(define (tancf d x k)
(define (iter i null-value)
(if (> i k)
null-value
(/ (square x) (- (d i) (iter (+ i 1) null-value)))))
(* (iter 1 1.0) x))
(define (square x) (* x x))
(define (tan-cf x k)
(define (d i)
(- (* 2 i) 1))
(tancf d x k))
(tan-cf 1 10000)
练习 1.40
解:
#|求平方|#
(define (square x) (* x x))
(define dx 0.001)
#|求导|#
(define (deriv g)
(lambda (x)
(/ (- (g (+ x dx)) (g x))
dx)))
#|牛顿法逼近零点|#
(define (newton-transform g)
(lambda (x)
(- x (/ (g x) ((deriv g) x)))))
#|fixed-point 该过程产生出不动点的过程 |#
(define (fixed-point f first-guess)
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) 0.00001))
(define (try guess)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next))))
(try first-guess))
#|返回这个过程|#
(define (newtons-method g guess)
(fixed-point (newton-transform g) guess))
(define (cubic a b c)
(lambda (x) (+ (* x x x) (* a x x) (* b x) c)))
#|给a b c 赋值|#
(newtons-method (cubic 1 1 1) 1)
>-1.0000000001211478
练习 1.41
解:
(define (double g)
(lambda (x) (g (g x))))
(define (inc x) (+ x 1))
((double inc) 1)
(((double (double double)) inc) 5)
>3
>21
练习 1.42
解:
(define (inc x) (+ x 1))
(define (square x) (* x x))
(define (compose2 a b)
(lambda (x)
(a (b x))))
((compose2 square inc) 6)
练习 1.43
解:
迭代:
(define (square x) (* x x))
(define (repeated f n)
(define (iter x n)
(if (<= n 0)
x
(iter (f x) (- n 1))))
(lambda (x)
(iter x n)))
((repeated square 2) 5)
>625
递归:
(define (square x) (* x x))
(define (repeated f n)
(define (iter x n)
(if (<= n 0)
x
(f (iter x (- n 1)))))
(lambda (x)
(iter x n)))
((repeated square 2) 5)
>625
练习 1.44
解:
(define dx 0.001)
(define (square x) (* x x))
(define (smooth f)
(lambda (x)
(/ (+ (f (- x dx)) (f x) (f (+ x dx))) 3)))
(define (repeated f n)
(define (iter x n)
(if (<= n 0)
x
(f (iter x (- n 1)))))
(lambda (x)
(iter x n)))
(define (smooth-repeated f n)
(lambda (x)
(((repeated smooth n) f) x)))
((smooth-repeated square 10) 5) ;;;f函数经过10次平滑后产生的过程应用到5上
>25.00000666666665
以后补下面两题