1、高阶函数的介绍
高阶函数的英文名称是Higher Order Function,它们是以函数为参数的函数。主要用于映射(mapping)、过滤(filtering)、归档(folding)和排序(sorting)表。高阶函数让程序更具模块性,让函数更加通用。
函数sort具有2个参数,一个是需要排序的表,另一个是定序(Ordering)函数。下面展示了按照大小将一个整数表正序排序。而<函数就是本例中函数的定序函数。
(sort‘(420 -130 138 983 0 298 783 -783) <)
;Value:(-783 -130 0 138 298 420 783 983)
通过灵活使用定序函数,我们可以写出更强大的函数。
(sort‘(783 298 -289 429 892 479 -197)
(lambda (x y) (< (modulo x 100)(modulo y 100))))
;Value:(-197 -289 429 479 783 492 892 298)
我们之前讲过,modulo函数用来求余。
Scheme并不区别过程和其他的数据结构,因此你可以通过将函数当作参数传递轻松的定义自己的高阶函数。而且Scheme并没有定义块结构的语法,因此使用lambda表达式作为一个块。
2、映射
映射是将同样的行为应用于表所有元素的过程。R5RS定义了两个映射过程:其一为返回转化后的表的map过程,另一为注重副作用的for-each过程。
map过程的格式如下:
(map procedurelist1 list2 …)
procedure是个与某个过程或lambda表达式相绑定的符号。作为参数的表的个数视procedure需要的参数而定。
(map+ ‘(1 3 5) ‘(2 4 6))
;Value:(3 7 11)
(map(lambda (x) (* x x)) ‘(1 2 3))
;Value:(1 4 9)
通过类比可以发现后者的lambda表达式相当于前者的+函数。只不过后者只有一个list,而前者有2个。如果想要像前者一样,让两个list中的元素一次相乘,除了用*外,也可以用lambda表达式。
(map(lambda (x y) (* x y)) ‘(1 2 3) ‘(2 4 6))
;Value:(2 8 18)
但是如果我们这样写:(map (lambda (x) (* x x)) ‘(1 2 3) ‘(1 3 5)),它并不会得出(1 4 9) (1 9 25)。
如果我们这样写:(map (lambda (x y) (* x x) (* y y)) ‘(1 2 3) ‘(1 3 5)),它得出的结果是(1 9 25),这是因为其只有一个返回值。但是(* x x)这一部分确实计算了。通过下面的例子我们可以确信这一点。
(map(lambda (x y) (let ((list1 (* x x)) (list2 (* y y)))
list1)) ‘(1 2 3) ‘(1 3 5))
;Value:(1 4 9)
(map(lambda (x y) (let ((list1 (* xx)) (list2 (* y y)))
list1 list2))‘(1 2 3) ‘(1 3 5))
;Value:(1 9 25) 这里同样是因为其只能有一个返回值。
而map函数最终的返回值以运算结果来判断。
(map- ‘(3 2 0) ‘(3 1 1 3))
;Value:(0 1 -1)
(map– ‘(3 2 0) ‘(3 1))
;Value:(0 1)
因为前者中list1中没有和list2中最后一个元素相对应的元素了,而后者中list2中没有和list1中最后一个元素相对应的元素。
(map(lambda (x y z) (* x x) (* y y) (* z z)) ‘(1 2) ‘(3 4 5) ‘(6 7 8 9))
;Value:(36 49)
而在这里例子中为什么最后的返回值不是(36 49 64 81),博主也不知道了,还请知道的网友留个回复。
for-each
for-each的格式与map一致,但是for-each并不返回一个具体的值,只是用于副作用。(副作用的解释)我们同样通过一个示例来展示。
(definesum 0)
;Value:sum
(for-each(lambda (x) (set! sum (+ sum x))) ‘(1 2 3 4))
;Unspecifiedreturn value
sum
;Value:10
如前所述,for-each并没有返回值。
3、过滤
尽管过滤函数并没有在R5RS中定义,但MIT-Scheme实现提供了keep-matching-items和delete-matching-item两个函数。注意item后加和不加s。
(keep-matching-items ‘(1 -2 3 -4 5)even?)
;Value: (-2 -4)
其中even?我们可以认为对应于上前文中sort函数的定序函数,当然,我们也可以用lambda来作为这个参数。
(keep-matching-items ‘(10 39 0 -100 76)(lambda (x) (<= 10 x 100)))
;Value: (10 39 76)
4、归档
同样在R5RS中没有定义归档函数,但MIT-Scheme提供了reduce等函数。
(reduce + 0 ‘(1 2 3 4)) ;⇒ 10
(reduce + 0 ‘(1 2)) ;⇒ 3
(reduce + 0 ‘(1)) ;⇒ 1
(reduce + 0 ‘()) ;⇒ 0
(reduce + 0 ‘(foo)) ;⇒ foo
(reduce list ‘() ‘(1 2 3 4)) ;⇒ (((1 2) 3) 4)
(define (sqrt-sum-sq ls)
(sqrt (reduce + 0(map (lambda (x) (* x x)) ls))))
5、排序
同样在R5RS中没有定义排序函数,而在MIT-Scheme中提供了sort(实为merge-sort实现)和quick-sort函数。这个函数我们在前面展示过,下面我们用sort函数,lambda,tan函数和<来写一个以tan(x)的大小升序排列的函数。
(define(sort-tan ls)
(sort ls (lambda (x y) (< (sin x) (siny)))))
;Value:sort-tan
(sort-tan‘(1 2 3 4 5 6))
;Value:(5 4 6 3 1 2)
6、apply函数
apply函数将一个过程应用于一个表,也就是将表展开作为过程的参数。该函数具有任意多个参数,但首末参数必须分别是一个过程和一个表。
(applymax ‘(-1 2 -3))
;Value:2
(apply* 1 2 ‘(3 4 5))
;Value:120