Program Structure 笔记3 (高阶过程以及lambda语句)
(作者:colinboy Email:cybbh@163.com) 2008.5.2
(内容难免出现错误或一些专业词汇使用不当,只是个人笔记,能理解总体内容就好)
Generalize Patterns(程序设计中存在的通用模式)
(define pi 3.1415926)
(define (square-area r) (* r r)) //定义square-area函数
(define (circle-area r) (* pi r r))
(define (sphere-area r) (* 4 pi r r))
我们发现上面定义的函数形式参数都相同,功能都是某个数和r的平方相乘,那么我们可以把上面函数写成一个更通用的函数,例如:
(define (area shape r) (* shape r r))
(define square 1)
(define circle pi)
(define sphere (* 4 pi))
上面定义了一个area函数,通过参数shape传入要计算的图形
(area square 5) --> 25
上面的调用等同于 (square-area 5)
对于上面的一些计算面积的函数,我们通过发现它们运算性质的一些共性来写出了一个通用函数,但是对于很多应用,往往各种同类应用中的区别通过调用的函数不同而体现.
例如:
(define (sumsquare a b)
(if (> a b)
0
(+ (* a a) (sumsquare (+ a 1) b)) ))
(define (sumcube a b)
(if (> a b)
0
(+ (* a a a) (sumcube (+ a 1) b)) ))
(sumsquare 4 6) --> 77
对于(sumsquare 4 6)等于4*4 + 5*5 + 6*6, sumcube与此类似
我们如何写出一个通用函数sum,可以计算各种图形特定区间之内的面积和?
具体实现如下:
(define (sum fn a b)
(if (> a b)
0
(+ (fn a) (sum fn (+ a 1) b))))
我们定义了一个sum过程,它需要3个参数才能工作,我们如何使用它??
首先我们需要定义2个求面积的函数
(define (square x) (* x x))
(define (cube x) (* x x x))
然后把定义的函数传入sum中就可以运行sum计算面积和了.
(sum square 4 6) --> 77
(sum cube 4 6) --> 405
什么?? 把一个函数当作参数?
在scheme中,函数是一种类型,是一种值,我们可以把它当作参数传给其他函数!
一些例子:
(define (evens nums)
(cond ((empty? nums) '())
((= (remainder (first nums) 2) 0)
(se (first nums) (evens (bf nums))) )
(else (evens (bf nums))) ))
(evens '(55 12 3 8 9 53)) --> (12 8)
evens用来判定一组数中哪些数是否是偶数. evens使用了递归.
-------------------------------
(define (ewords sent)
(cond ((empty? sent) '())
((member? 'e (first sent))
(se (first sent) (ewords (bf sent))) )
(else (ewords (bf sent))) ))
(ewords '(help what hi hell me you send)) --> (help hell me send)
ewords用来检测一组单词中哪些单词中含有字母"e".
-------------------------------
(define (keep pred sent)
(cond((empty? sent) '())
((pred (first sent))(se (first sent)(keep pred (bf sent))))
(else (keep pred (bf sent)))))
(keep number? '(1 haha 88 go ok 3)) --> (1 88 3)
keep有2个参数,第一个参数为检测条件,第二个参数为要检测的数据,上面我们检测条件为number?(number?是一个原始内置函数), 那么keep函数会检查输入的一组数据中哪些是数字.
对于上面使用一个函数当作参数的函数我们成为高阶函数(Higher-Order Function)或者高阶过程(Higher-Order Procedure).
在数学领域中,经常讨论函数,例如
f(x) = 2x + 6
上面定义了一个名为f的函数,我们会说函数f是......等等内容,有时我们希望讨论一些没有名字的函数,我们通常会直接写出此函数的公式,例如
x |--> 2x + 6
|-->是一种映射.
在scheme中,对于上面2种函数的表示方法为:
对于f(x) = 2x + 6:
(define (f x) (+ (* 2 x) 6))
对于x |--> 2x + 6:
(lambda (x) (+ (* 2 x) 6))
lambda是定义一个没有名字的函数,它不和环境中的任何名字相关联.
对于前面定义的keep函数,我们可以使用如下方法调用:
(keep (lambda (wd) (equal? (first wd) (last wd))) '(
california
ese ope
alaska
)) --> (ese
alaska
)
此调用的功能是对于输入的一组单词,检测出开头字母和结尾字母相同的单词.
define会返回一个函数名称!!
lambda会返回一个过程!!
(define (square x) (* x x))
(define square (lambda (x) (* x x)))
上面2个语句的作用是相同的,在scheme中,解释器会把第一个语句转换成第二个语句执行.
我们可以用lambda定义带有多个参数的过程,例如:
(define f (lambda (x y) (+ (* 2 x) y)))
(f 4 3) --> 11
(f 5 6) --> 16
lambda语句是一个特殊形式(special form).因为它不会计算所有实际参数表达式的值.
概念:
First class data
First class data在语言中可以当作:
1.一个变量的值
2.一个函数的参数
3.一个函数的返回值
4.一个集合的成员
例如对于大多数计算机语言,整数都是First class data,在scheme中,数字,表(list),字符,过程等都是First class data.
((lambda (x) (* x x)) 5) --> 25
上面的语句执行后会返回25,为什么??
前面我们讨论了如何把一个函数当作另一个函数的参数进行操作,那么如果一个函数的返回值为一个函数会怎么样呢?
例1
(define (compose f g)
(lambda (x) (f (g x))))
我们如何使用定义的compose函数呢?
(define second (compose first bf))
(second '(this is how to use)) --> is
我们定义了一个second函数,second会调用(compose first bf), compose会返回一个函数,所以second等同于:
(lambda (x) (first (bf x)))
(define (twice f) (compose f f))
(define third (twice square))
(third 5) --> 625
对于上面的third函数,等同于(lambda (x) (square (square x)))
相比上面的例子,一个更简明的如何使用返回函数的函数例子:
(define (make-adder num)
(lambda (x) (+ x sum)))
(define 3+ (make-adder 3))
(3+ 4) --> 7
(3+ 1) --> 4
注意:lambda会返回一个过程,这是理解上面例子的关键.