Trong ch ng 1, ta đã đnh ngh a hàm foldr «bao qua ph i» dùng đ tính toán tích lu k t qu trên các ph n t c a m t danh sách. Sau đây ta đnh ngh a trong Scheme hai hàm fold là foldl (left) và foldr (right) cùng nh n vào m t hàm f hai tham đ i : m t ph n t xu t phát a và m t danh sách L, đ tr v k t qu là áp d ng liên ti p (lu k ) hàm f cho a và v i m i ph n t c a L. Hai hàm khác nhau ch hàm foldl l y l n l t các ph n t c a L
t trái qua ph i, còn hàm foldl l y l n l t các ph n t c a L t ph i qua trái : (define (foldl f a L) (if (null? L) a (foldl f (f a (car L)) (cdr L)))) (define (foldr f a L) (if (null? L) a (f (car L) (foldr f a (cdr L))))) (foldl cons 0 ’(1 2 3 4 5)) --> ’(((((0 . 1) . 2) . 3) . 4) . 5) (foldr cons 0 ’(1 2 3 4 5)) --> ’(1 2 3 4 5 . 0)
IV.2.6. Tham đ i hoá t ng ph n
Nh đã trình bày ch ng 1 v nguyên lý l p trình hàm, k thu t tham đ i hóa t ng ph n (currying) cho phép dùng bi n đ truy c p đ n các hàm có s tham bi n b t k f(x1, ... , xn).
Ch ng h n, hàm hai bi n f(x, y)đ c xem là hàm m t bi n x tr v giá tr là hàm y :
x →(y → f(x, y))
Gi s xét hàm max tìm s l n nh t trong th vi n Scheme, v i n=2 : (max (* 2 5) 10)
--> 10
S d ng k thu t Currying, ta vi t : (define (curry2 f)
(lambda (x) (lambda (y) (f x y)))) (((curry2 max) (* 2 5)) 10) --> 10 V i n=3, ta c ng xây d ng t ng t : (define (curry3 f) (lambda (x) (lambda (y) (lambda (z) (f x y z)))))
((((curry3 max) (* 2 5)) 10) (+ 2 6)) --> 10
T đó ta có th xây d ng cho hàm nđ i b t k . u đi m c a k thu t Currying là có th
đ c t m t hàm ngay khi m i bi t giá tr c a tham s th nh t, ho c các tham s đ u tiên cho các hàm có nhi u h n hai tham đ i.
i v i hàm m t bi n, u đi m c a k thu t Currying là ng i ta có th t h p tu ý các hàm mà không quan tâm đ n các tham đ i c a chúng.
IV.2.7. nh ngh a đ quy c c b
Trong ch ng tr c, ta đã làm quen v i khái ni m hàm b tr đ đnh ngh a các hàm Scheme. Hàm b tr có th n m ngay trong hàm c n đnh ngh a, đ c g i là hàm c c b . Ng i ta có th s d ng d ng hàm letrec trong th vi n Scheme đ đnh ngh a đ quy m t hàm c c b . Ch ng h n ta c n xây d ng m t danh sách các s t nhiên 0..n v i n cho tr c nh sau :
; iota : number −> list(number) ; ho c iota : n −> (0 1 2 ... n) (define (iota n)
(if (zero? n ) ’(0)
(append (iota (- n 1)) (list n)))) (iota 10)
--> '(0 1 2 3 4 5 6 7 8 9 10)
nh ngh a trên đây đúng đ n, tuy nhiên vi c s d ng hàm ghép danh sách append làm t n b nh , do luôn luôn ph i thêm các ph n t m i vào cu i m t danh sách. Vì v y ý t ng c i biên là làm ng c l i v n đ : xây d ng hàm cho phép nh n m t s mđ tr v danh sách các s t nhiên t mđ n n là (m m+1 ....n). Thay vì s d ng hàm ghép danh sách, ta s d ng consđ xây d ng hàm b tr nh sau :
(define (m-to-n m n) (if (< n m) ’() (cons m (m-to-n (+ m 1) n)))) (m-to-n 3 10) --> ’(3 4 5 6 7 8 10)
Do hàm m-to-n không dùng đâu khác, nên c n đ t bên trong hàm iota. Sau khi đnh ngh a, c n g i v i tham đ i m=0 : (define (iota n) (define (m-to-n m n) (if (< n m) ’() (cons m (m-to-n (+ m 1) n)))) (m-to-n 0 n)) (iota 10) --> '(0 1 2 3 4 5 6 7 8 9 10)
Gi s thay vì đnh ngh a hàm c c b m-to-n, ta s d ng d ng let đ t o ra l n l t các s t nhiên k t m. Tuy nhiên không c n dùng đ n tham bi n n vì nđã đ c c p b i hàm iota :
(define (iota n)
(let ((m-to-n (lambda (m) (if (< m n) ’() (cons m (m-to-n (+ m 1))))))) (m-to-n 0))) (iota 10) --> ’()
Ta th y k t qu sai vì l i g i m-to-n trong hàm ch d n đ n th c hi n let m t l n mà không th c hi n g i đ quy. kh c ph c, ta s d ng d ng letrec, là d ng let đ c bi t
đ t o ra l i g i đ quy. Cú pháp c a letrec gi ng h t let c ng g m ph n liên k t và ph n thân :
(letrec ((v1 e1 ) ... (vk eN)) s)
Các bi n vi, i=1..N, đ c gán giá tr eiđ sau đó th c hi n ph n thân s là m t bi u th c nào đó. Tuy nhiên m i phép gán bi n có th «nhìn th y» l n nhau, ngh a là khi tính bi u th c ei thì có th s d ng các bi n vi v i i, j tu ý. Ngh a là không gi ng hoàn toàn let, các bi n c c b v1, ... , vN đ u đ c nhìn th y trong t t c các bi u th c e1, ... , ek. Tuy nhiên, c n chú ý r ng m i bi u th c ej đ c tính mà không c n tính giá tr c a bi n vj, khi
ej là m t bi u th c lambda.
Nh ng ngh a này, d ng letrec th ng đ c s d ng đ đnh ngh a các th t c đ quy
t ng h (mutually recursive).. Ch ng h n, các v t odd? và even? là tr ng h p đi n
hình cho các hàm th a nh n đnh ngh a đ quy t ng h . Sau đây là ví d s d ng letrec
đ đnh ngh a t ng h hai th t c c c b ki m tra m t s nguyên là ch n (even) hay l (odd) mà không s d ng hai hàm th vi n c a Scheme là even? và odd? :
(letrec ((local-even? (lambda (n) (if (= n 0) #t (local-odd? (- n 1))))) (local-odd? (lambda (n) (if (= n 0) #f (local-even? (- n 1)))))) (list (local-even? 27) (local-odd? 27))) --> ’(#f #t)
Bây gi hàm iotađ c đnh ngh a l i nh sau : (define (iota n)
(letrec
((m-to-n (lambda (m) (if (< n m)
(cons m (m-to-n (+ m 1 ))))))) (m-to-n 0)))
(iota 10)
−−> ’(0 1 2 3 4 5 6 7 8 9 10)
S d ng ph i h p d ng letrec và lambda nh sau :
(lambda (x1 ... xN) (define f1 e1) ... (define fN eN) s) ⇔ (lambda (x1 ... xN) (letrec (f1 e1) ... (fN eN)) s)) IV.3 X lý trên các hàm IV.3.1. Xây d ng các phép l p
Trong m c tr c, ta đã đnh ngh a hàm list-it s d ng k thu t tích lu k t qu đ
t o ra m t c u trúc l p trình gi ng nhau áp d ng cho nhi u hàm khác nhau. Sau đây, ta s xây d ng các hàm l p m i là append-map, map-select, every và some đ m r ng th vi n các hàm c a Scheme (v n ch có hai th t c l p có s n là map và for-each).
1. Hàm append-map
Khi m t hàm f nh n tham đ i là các ph n t c a m t danh sách đ tr v các giá tr là danh sách, ng i ta c n n i ghép (concatenation) các danh sách này. Ta xây d ng hàm append-map nh sau :
(define (append-map f L)
(apply append (map f L)))
Áp d ng hàm append-map, ta có th xây d ng hàm flatting đ làm ph ng («cào b ng») m t danh sách phù h p khác r ng theo nguyên t c : ghép k t qu làm ph ng các ph n t c a danh sách m c th nh t, k t qu làm ph ng c a nguyên t là danh sách (thu g n v nguyên t ) :
(define (flatting s) (if (list? s)
(append-map flatting s) (list s)))
(flatting ’(a (b c) ((d (e))) "yes" ()))
−−> ’(a b c d e "yes") (flatting ’a)
−−> ’(a)
(flatting 10)
(flatting ’())
−−> ’()
2. Hàm map-select
Nhi u khi, ng i ta ch mu n áp d ng hàm map cho m t s ph n t c a m t danh sách tho mãn m t v t p?nào đó mà thôi, ngh a là s d ng map có l a ch n. Ta xây d ng hàm map-select v i ý t ng nh sau : s d ng append-map và gán giá tr ’() cho các ph n t không tho mãn v t , và do v y các giá tr r ng này không đ a vào danh sách k t qu cu i cùng khi hàm tr v . (define (map-select f L p?) (append-map (lambda (x) (if (p? x) (list (f x)) ’())) L)) (map-select (lambda (x)(/ 1 x)) ’(a 3 0 5 7 9) (lambda (x)
(and (number? x) (not (zero? x)))))
−−> ’(1/3 1/5 1/7 1/9)
(map-select sqrt '(1 2 3 4 5) odd?)
−−> ’(1. 1.73205 2.23607) 3. Các hàm every và some
Khi các đ i s c a phép h i lôgic and là các giá tr c a hàm f nào đó, ta có th đnh ngh a hàm everyđ m r ng and nh sau :
(every f ’(e1 ... eN)) = (and (f e1) ... (f eN))
Tuy nhiên ta không th đnh ngh a every m t cách tr c giác là áp d ng phép and cho danh sách các giá tr c a f :
(define (every f L)
(apply and (map f L)))
B i vì hàm apply không nh n and làm tham đ i. Trong Scheme, and không ph i là hàm mà là m t d ng đ c bi t. Ta đnh ngh a every theo cách đ quy truy n th ng nh sau :
(define (every f L) (if (null? L) #t (and (f (car L)) (every f (cdr L))))) (every even? '(0 2 4 6 8)) −−> #t (every number? '(1 3 a 5)) −−> #f
M t cách t ng t , ta xây d ng hàm some đ m r ng phép tuy n lôgic or b ng cách thay th and b i or có d ng: (some f ’(e1 ... eN)) = (or (f e1) ... (f eN)) Hàm some nh sau : (define (some f L) (if (null? L) #f (or (f (car L)) (some f (cdr L))))) (some number? ’(1 3 a 5)) −−> #t (some odd? ’(0 2 4 6 8)) −−> #f
IV.3.2. Trao đ i thông đi p gi a các hàm
Sau khi đnh ngh a các hàm và cài đ t các ki u d li u, ng i s d ng có th thao tác tr c ti p trên d li u b ng cách s d ng k thu t l p trình truy n thông đi p (message transmission). Thay vì xem d li u nh là m t giá tr ki u đ n v (m t s nguyên, m t b đôi, m t danh sách, v.v ... ), ta có th xem d li u nh là m t hàm nh n các thông đi p và th c hi n tính toán tu thu c vào thông đi p đã nh n. Ng i ta g i đây là hàm b ghi (switch function). Nh ng hàm th y đ c t bên ngoài ch còn là m t l i g i v i m t thông đi p đ c bi t. Ta xét l i ví d v x lý các s h u t ch ng tr c. Ta đã khai báo các hàm t o s h u t , xác đnh t s và m u s nh sau :
functions
create-rat : integer × integer → rational numer : rational → integer
denom : rational → integer Bây gi ta đnh ngh a l i hàm create-rat có d ng nh sau :
create-rat : (integer × integer) → (symbol → Integer)
Hàm create-rat s t o ra m t hàm nh n vào m t thông đi p (ki u ký hi u) đ tr v m t s nguyên, tu theo n i dung thông đi p cho bi t đó là t s hay m u s :
(define (create-rat x y) ; precondition: y ≠ 0
(define g (gcd x y)) ; gcd là c s chung l n nh t c a x, y (define N (quotient x g))
(define D (quotient y g)) (lambda (message)
(cond
((eq? message ’numerator) N) ((eq? message ’denominator) D)
(else (error ”unknown message” message))))) ((create-rat 9 15) ’numerator)
((create-rat 9 15) ’denominator) --> 5
((create-rat 9 15) ’denom)
--> *** Error: "unknown message" (denom) (create-rat 9 15)
--> *** Error: ’#{Procedure 6700 (unnamed in create-rat)} Ta ti p t c đnh ngh a hai hàm numer và denom s g i đ n hàm create-rat : ; numer : (integer × integer) → integer
(define (numer R) (R ’numerator))
; denom : (integer × integer) → integer (define (denom R) (R ’denominator)) (numer (create-rat 9 15)) --> 3 (denom (create-rat 9 15)) --> 5
V i cách xây d ng các hàm x lý các s h u t trên đây, ng i s d ng ch có th g i đ n m t hàm nh m t thông đi p message : l i sai do m t l i g i hàm nào đó, vô tình hay c ý, đ u không th x y ra. Trong tr ng h p m t thông đi p c n các tham đ i b sung, hàm b ghi s d n v m t hàm c n ph i th c hi n v i nh ng tham đ i này.
N u c n thêm m t b ki m tra =rat, ch c n thêm m nh đ sau đây vào đi u ki n ki m tra message c a hàm create-rat (tr c else) trên đây :
((eq? message ’=rational)
(lambda(R) (= (* N (denom R)) (* D (numer R)))))
Lúc này, ta có th vi t hàm =rat tr v m t hàm m t tham đ i là s h u t c n so sánh nh sau :
(define (=rat R1 R2)
((R1 ’=rational) R2)) ; áp d ng hàm tr v v i R2 (=rat (create-rat 1 3) (create-rat 1 3)) --> #t
(=rat (create-rat 2 3) (create-rat 3 2)) --> #f
K thu t l p trình xem d li u nh là m t hàm nh n các thông đi p đ th c hi n tính toán th ng khó s d ng, đòi h i ng i l p trình ph i qu n lý t t ý đ x lý theo n i dung thông
đi p. T t c nh ng gì là d li u đ u có th t o ra hàm nh lambda nh ng r t khó nh n bi t
đ c nh ng thông đi p nào s tho mãn hàm. Ch ng h n sau đây là m t ví d s d ng b đôi nh ng thay đ i hai hàm car là cdr :
(define (cons x y) (lambda (m)
(cond
((eq? m ’car) x) ((eq? m ’cdr) y)
(else (error "unknown message"))))) (define (car D) (D ’car))
(define (cdr D) (D ’cdr)) (cdr (cons ’tom ’jerry)) --> ’jerry
(car (cons '’tom '’jerry)) --> ’tom
(car ’(1 2 3))
--> ERROR: attempt to call a non-procedure
IV.3.3. T h p các hàm
T khái ni m hàm b c cao, ta có th đnh ngh a các hàm t h p (function composition) nh sau :
; gºf : (T1 → T2)→T3
Ho c t h p tr c ti p ngay trong tên hàm : (define (compofg x ) (g (f x)) ch ng h n : (define (cacddddr L) (car (cddddr L))) (cacddddr ’(a b c d e f)) −−>’e Ho c s d ng lambda đ t o ra d ng m t «l i h a» : (define (compos g f) (lambda (x) (g (f x)))) ch ng h n :
((compos car cddddr) ’(a b c d e f g ))
−−> ’e
(define fifth
(compos car cddddr)) (fifth ’(a b c d e f g ))
−−> ’e
Nh khái ni m b c c a hàm, nhi u sai sót có th đ c phát hi n. Gi s ta xét các hàm map và cdr làm vi c v i d li u ki u danh sách :
map : (T1 → T2) × List(T1) → List(T2) cdr : List(T) → List(T)
N u áp d ng hàm map là hàm b c 2 có cdr làm tham đ i s gây ra l i : (map cdr (list 1 2 3))
--> ERROR: cdr: Wrong type in arg1 1
Trong đnh ngh a hàm cdr, danh sách có ki u List(T), nên danh sách (1 2 3) ph i có ki u List(Integer). Nh v y, đ áp d ng đ c hàm map, c n ph i có :
T1 = List(T) = Integer
f : (Number → T1) → T2 ta đnh ngh a hàm :
(define (f g) (g 2)) thì l i g i sau đây là sai :
(f f)
--> ERROR: Wrong type to apply: 2 Tuy nhiên nh ng l i g i sau đây l i cho k t qu đúng :
(f list)
--> '(2) ; t o danh sách m t ph n t (f sqrt)
--> 1.41421 ; tính c n b c hai m t s nguyên
Ví d sau là m t đnh ngh a hàm s d ng m t hàm khác làm tham đ i nh ng th c t , tham đ i này ch ng đóng vai trò gì trong vi c tính toán thân hàm, ch có m t cho «ph i phép» xét v m t cú pháp : (define (f g n) (if (= n 0) 1 (* n (g g (- n 1))))) (define (mystery n) (f f n)) (mystery 5) --> 120 (f f 5) --> 120
Ng i đ c có th nhìn nh n ra ngay mystery là hàm tính giai th a ! Xét v phép toán,
đnh ngh a trên t ra h p lý (tính đúng giai th a). Tuy nhiên, m i ngôn ng có đnh ki u m nh s t ch i đnh ngh a này, vi không th xác đnh đ c b c c a hàm f và vai trò c a nó trong thân hàm. M t cách t ng t , ng i đ c có nh n xét gì khi thay đ i l i đnh ngh a hàm tính giai th a nh sau : (define (fac g h n) (if (= n 0) 1 (* n (h h h (- n 1))))) Có th thay đ i dòng cu i cùng thành (g g g ... ) ? IV.3.4. Các hàm có s l ng tham đ i b t k
Khi g i th c hi n m t hàm, b di n d ch Scheme đ t t ng ng các tham đ i hình th c v i các tham đ i th c s . Sau đây là các d ng đnh ngh a hàm có nhi u tham đ i tu ý và d ng s d ng phép tính lambda t ng đ ng.
D ng đnh ngh a 1 :
(define (f x y z) ... ) t ng đ ng v i :
Ví d v i l i g i : (f 1 (+ 1 2) 4)
các tham đ i hình th c l n l t đ c nh n giá tr : x=1, y=3, z=4.
D ng đnh ngh a 2 : (define (g.L) ... ) (chú ý g là tên hàm, có d u cách tr c và sau d u ch m) t ng đ ng v i : (define g (lambda L ... )) Ví d v i l i g i : (g 1 2 3 4 5)
tham đ i hình th c là danh sách Lđ c l y giá tr L = ’(1 2 3 4 5).
D ng đnh ngh a 3 : (define (h x y . z) ... ) t ng đ ng v i : (define h (lambda (x y . z) ... ) Ví d v i l i g i : (h 1 2 3 4)
các tham đ i hình th c đ c l y giá tr nh sau : ((x y . z)đ c so sánh v i (1 2 3 4), t đó, x=1, y=2, z=(3 4)
Trong 3 d ng đ nh ngh a trên đây, hàm f th a nh n đúng 3 tham đ i, hàm g có s l ng tham đ i tu ý, hàm h ph i có t i thi u 2 tham đ i. Ta có th d dàng đnh ngh a hàm list theo d ng 2 :
(define (list . L) L)
(list 1 2 3 4 5 6 7 8 9 0) --> ’(1 2 3 4 5 7 8 9 0)
Ta có th m r ng m t hàm hai tham đ i đ đnh ngh a thành hàm có nhi u tham đ i. Ví d , t hàm c ng + là phép toán hai ngôi, ta đnh ngh a hàm add đ c ng d n các ph n t c a tham đ i là m t dãy s nguyên tu ý :
(define (add . L) ; L : List(Number) (define (add-bis L R) (if (null? L) R (add-bis (cdr L) (+ (car L) R)))) (add-bis L 0)) (add 2 3 4 5) --> 14
Áp d ng hàm apply, hàm add có th đnh ngh a theo cách khác nh sau : (define (add . L)
(if (null? L) 0
(+ (car L) (apply add (cdr L ))))) (add 1 2 3 4 5)
--> 15
(apply add L) --> 15 M t cách t ng quát, các d ng đnh ngh a 2 và 3 có th vi t d i d ng : (define (f v0 v1 . . vn- 1 .vN) s) t ng đ ng v i d ng lambda : (define (f) (lambda (L) s))
trong đó thân hàm s là m t bi u th c, L là m t danh sách các đ i tu ý.