Ta c n xây d ng m t danh sách m i t danh sách đã cho mà không còn các c p ngo c bên trong nh sau :
(aflatten ’(a (b c) () ((d)) e)) --> (a b c d e)
Ta có :
(define (aflatten L) (cond ((null? L) ’())
; N u car c a danh sách đã cho là m t danh sách, thì làm ph ng nó ; r i ghép k t qu v i k t qu làm ph ng c a cdr.
((list? (car L)) (append (aflatten (car L)) (aflatten (cdr L)))) ; N u car c a danh sách đã cho không ph i là m t danh sách, ; thì thêm ph n t này vào đ u k t qu làm ph ng c a cdr. (else (cons (car L) (aflatten (cdr L))))))
2. Tính t ng các s có m t trong danh sách
X y ra b n tr ng h p sau :
− N u danh sách đã cho là r ng, thì k t qu c ng là danh sách r ng
− N u car là m t s , thì c ng s này v i t ng c a t t c các s c a cdr.
− N u car là m t danh sách khác r ng, thì tính t ng c a t t c các s c a car r i c ng k t qu này v i t ng c a t t c các s c a cdr. − N u car là m t danh sách r ng ho c là m t đ i t ng khác s thì ch tính t ng c a t t c các s c a cdr. Sau đây là ch ng trình : (define (Listsum* L) (cond ((null? L) 0)
((number? (car L)) (+ (car L) (Listsum* (cdr L)))) ((pair? (car L)) (+ (Listsum* (car L))
(Listsum* (cdr L)))) (else (Listsum* (cdr L)))))
(Listsum* ’(a (4) 5 ((6 b)) 8)) --> 23
3. Lo i b kh i danh sách m t ph n t các m c khác nhau
Cho m t danh sách L và m t bi u th c s. C n lo i b kh i L các ph n t có giá tr b ng s. Ta xây d ng hàm remove1 x lý 4 tr ng h p nh sau :
− N u danh sách đã cho là r ng, thì k t qu c ng là danh sách r ng
− N u ph n t đ u tiên là s, thì lo i b nó.
− N u ph n t đ u tiên là m t danh sách thì x lý nó và ph n còn l i cdr sau đó ghép k t qu l i.
− N u ph n t đ u tiên không ph i là s, thì x lý ph n còn l i cdr. Ch ng trình nh sau :
(define (remove1 s L)
(cond ((null? L) '()) ; danh sách r ng không ch a s ((equal? s (car L)) ; ph n t đ u tiên là s
(remove1 s (cdr L)))
((list? (car L)) ; ph n t đ u tiên là m t danh sách (cons (remove1 s (car L)) (remove1 s (cdr L)))) (else (cons (car L ) ; ph n t đ u tiên khác s
(remove1 s (cdr L)))))) (remove* ’a ’(b a ((a) b) c (a) d)) --> (b (() b) c () d)
4. Ngh ch đ o danh sách
Xây d ng hàm reverse1 ngh ch đ om i ph n t m i m c c a m t danh sách : (reverse1 ’(a (b c d) e)
--> (e (d c b) a)
Trong ví d trên, ta đã ngh ch đ o các ph n t c a danh sách con (b c d). X y ra ba tr ng h p nh sau :
− N u ph n t đ u tiên c a danh sách là m t danh sách, thì ta ngh ch đ o các ph n t c a ph n còn l i cdr r i ghép k t qu này v i k t qu ngh ch đ o c a ph n t đ u tiên car.
− N u ph n t đ u tiên không ph i là m t danh sách, thì ta ngh ch đ o các ph n t c a cdr r i ghép k t qu này v i ph n t đ u tiên.
Hàm reverse1 g i hàm append1đ thêm m t ph n t vào cu i danh sách : (define (reverse1 L)
(cond ((null? L) '()) ((list? (car L))
(append1 (reverse1 (car L)) (reverse1 (cdr L)))) (else (append1 (car L) (reverse1 (cdr L))))))
(reverse1 '(a b ((e f) g) c (i j) d)) --> '(d (j i) c (g (f e)) b a)
5. So sánh b ng nhau
Ki u tr u t ng c a d ng th c so sánh b ng nhau (equality) đ c đnh ngh a nh sau : = : number × number → boolean
eq? : symbol × any → boolean ho c :
eq? : any × symbol → boolean char=? : character × character → boolean string=? : string × string → boolean Sau đây là b ng so sánh b ng nhau theo ki u d li u Scheme.
D li u Phép so sánh Ví d
Ki u s number =, <, <=, >, >= (= 2 2) --> #t Ki u ký t character char=? (char=? #\A #\a)
--> #f
Ki u chu i string string=? (string=? "123" "123") --> #t
Ki u b t k :
boolean, symbol, number, character, empty list, pair, vector, string, procedure
eqv?, eq?
(eqv? #\A #\A) --> #t
(eqv? 'toto 'toto) --> #t (eqv? "123" "123") --> #t Ki u pair, vector, string equal? (equal? "123" "123") --> #t
V t eqv?, hay eq?, dùng đ so sánh các ki u d li u b t k , k t qu s là #t n u chúng cùng m t đ i t ng. Riêng ki u d li u danh sách nên s d ng v t so sánh string=?.
(eq? 123 123) --> #t
(eq? 123456789012345678901 123456789012345678901) --> #f (eq? ’(1 . 2) ’(1 . 2)) --> #t (define (f L) (eq? L L)) (f '(#\a . #\a)) --> #t
Ta có th l i đnh ngh a v t so sánh t ng quát equal? m t cách đ n gi n h n nh sau : (define (equal? V1 V2)
(cond
((eq? V1 V2) ; x lý các tr ng h p boolean, symbol và ’() #t)
((and (number? V1) (number? V2)) (= V1 V2))
((and (char? V1) (char? V2)) (char=? V1 V2))
((and (string? V1) (string? V2)) (string=? V1 V2))
((and (pair? V1) (pair? V2))
(and (equal? (car V1) (car V2)) (equal? (cdr V1) (cdr V2)))) (else #f))) ; các đ i t ng có b n ch t khác nhau (equal? #\a #\a)
--> #t (equal? "123" "123") --> #t (equal? '(1 . 2) '(1 . 2)) --> #t (equal? '(1 2 3 4 5) '(1 2 3 4 5) ) --> #t III.4.3. Bi u di n danh sách
III.4.3.1. Bi u di n danh sách b i ki u b đôi
M t danh sách khác r ng là m t b đôi đ c bi t : ch ng h n danh sách có m t ph n t (a) chính là b đôi ’(a . ()) có ph n t th nh t car là a và ph n t th hai cdr là m t danh sách r ng. M t cách t ng quát, danh sách :
(s1 s2 ... sn)
đ c bi u di n b i ki u d li u b đôi :
(s1 . (s2 . ( ... (sn . ( ) ) ... )
Nh v y, m t danh sách ho c có th r ng, ho c có th là m t b đôi có cdr l i là m t b
đôi khác. Các thành ph n c a m t b đôi c ng có th là các b đôi. Ví d : (define x (cons ’a ’b))
y ; xem n i dung c a y --> ((a . b ) . c)
Bi u di n d ng cây (v đ n gi n) c a các b đôi nh sau :
a b c
B đôi (a . b) a b
B đôi ((a . b) . c)
Hình III.5. Bi u di n d ng cây các b đôi.
Nói cách khác, ta đã m r ng đnh ngh a c a s-bi u th c là nh ng b đôi. L y l i giá tr c a x = (1 . 2) trong ví d trên đây là m t b đôi, ta có :
(pair? y)
−-> #t
(pair? ’()) ; danh sách r ng không ph i là m t b đôi
−-> #f
(pair? ’(a b)) ; danh sách không r ng là m t b đôi
−-> #t Ngh a là ph n t c a b đôi có th ki u b đôi. in ra đ n gi n, m i l n m t c p t o thành m t danh sách, Scheme h n ch s d u ch m đ a ra : ’(a . (1 2)) −-> ’(a 1 2) ’(a . (b . (c . d))) −-> (a b c . d)
Khi m t danh sách đ c bi u di n d i d ng cây hình «cái cào» (rake), các ph n t c a l n l t là tr ng car c a các b đôi, tr ng cdr là danh sách r ng cu i cùng.
sN s2
s1
( )
Hình III.6. Bi u di n danh sách b i các b đôi.
Các b đôi có th đ c ti p c n b i nhi u con tr (m i tên) khác nhau. Ch ng h n, b đôi tr b i C trong ví d sau đây c ng đ c tr b i m t ph n t c a b đôi m i đ c xây d ng (là 5 và C) do con tr D tr t i : (define C (cons 1 2)) (define D (cons 3 C)) D ; xem n i dung c a D --> (3 1 . 2) N u x y ra đ t bi n giá tr c a b đôi tr b i C thì c ng d n đ n s đ t bi n d li u tr b i D :
(set-car! C 5) D ; xem n i dung c a D --> (3 5 . 2) D 1 2 C 3
Hình III.7. Nhi u m i tên cùng bi u di n m t con tr .
Ta có th làm thay đ i b t k đ i t ng nào đ c xây d ng t ki u d li u b đôi : m t danh sách, m t cây, v.v... Trong các ng d ng c a l p trình hàm, ng i ta th ng s d ng b
đôi đ mô hình hoá các đ i t ng có tr ng thái thay đ i theo th i gian. D 1 2 5 C 3 Hình III.8. t bi n c a C c ng là đ t bi n c a D.
Ng i ta c ng có th ti t ki m đ c nhi u b nh b ng cách bi u di n m i b đôi trong m t đ n v nh . Sau đây ta xét m t ví d v kh n ng x y ra hi u ng ph khi th c hi n các phép đ t bi n trên các danh sách.
(define X (list 1 2 3 4))
(define Y (cdr X)) ; X và Y có cùng danh sách con (2 3 4) (set-car! X 5) ; thay đ i X mà không nh h ng đ n Y
X --> (5 2 3 4) Y --> (2 3 4) ( ) 1 2 3 4 5 X Y 6 Hình III.9. t bi n gây ra hi u ng ph . (set-car! Y 6) ; đ t bi n gây ra hi u ng ph đ i v i X X --> (5 6 3 4)
Y
--> (6 3 4)
Tuy nhiên, sau khi gán l i ph n cdr c a X cho m t danh sách m i, thì con tr Y v n tr v danh sách c , kông thay đ i :
(set-cdr! X (list 7 8)) ;X và Yđ c l p (không chung nhau b đôi nào) X
--> (5 7 8) Y
--> (6 3 4)
S d ng hàm đ t bi n set-cdr!, ta có th t o ra các danh sách n i vòng quanh v i nhau nh sau : (define X (list 1)) X --> ’(1) (set-cdr! X X) (car X) --> 1 X --> 1 1 1 1 1 ... 1 1 1 ... ; vô h n l n !
Các hàm append, reverse, map (s xét ch ng sau), ... th ng r i vào tr ng thái qu n n u m t trong các tham đ i là m t danh sách n i vòng.
( ) X
1
Hình III.10. t bi n t o danh sách n i vòng có th gây ra qu n vô h n.
Sau đây ta xét m t ví d s d ng đ t bi n đ ghép danh sách. Gi s cho hai danh sách L1 và L2, ta c n ghép chúng đ nh n đ c m t danh sách m i. G i |L1| là đ dài (length) c a danh sách L1, ta có hai gi i pháp, m t gi i pháp không s d ng đ t bi n, nh sau :
Cách 1 : t o ra |L1| b đôi
; concat : List(T) ´ List(T) ® List(T) (define (concat L1 L2)
(if (null? L1) L2
(cons (car L1) (concat (cdr L1) L2)))) (concat '(1 2) '(3 4 5))
--> (1 2 3 4 5)
Cách 2 : s d ng đ t bi n.
Gi s đ đ n gi n, L1 khác r ng và k t qu không t o ra b đôi m i. Nh đ t bi n, b
; concat! : List(T) ´ List(T) ® List(T) (define (concat! L1 L2)
(define (last-doublet L) ; tr v b đôi cu i cùng c a L là null (if (null? (cdr L)) L (last-doublet (cdr L)))) (set-cdr! (last-doublet L1) L2)) (define x ’(1 2 3)) (define y ’(4 5)) (concat! x y) x --> (1 2 3 4 5) y --> (4 5) III.4.3.2. Danh sách k t h p 1. Khái ni m danh sách k t h p
Các b đôi th ng đ c dùng đ đnh ngh a danh sách k t h p (association list), vi t t t
là alist. ó là m t danh sách g m các ph n t có d ng b đôi đ ng nh t nh sau :
((c1 . v1)... (cn . vn)
Các alist th ng đ c dùng đ bi u di n các c u trúc d li u nh b ng (table), t đi n
(dictionary), các hàm, v v... Khi mu n k t h p m t khóa cj v i m t giá tr vj, ng i ta vi t (cj . vj) đ t o ra m t ph n t c a danh sách k t h p. Th vi n Scheme có hàm assq :
(assq s alist)
s d ng phép so sánh eq? đ tr v ph n t đ u tiên c a danh sách k t h p alist tho mãn
đi u ki n có car là khóa s. N u không tìm th y khoá nào nh v y, giá tr tr v là #f : (assq ’a ’((b . 2) (a . 1) (c . 3) (a . 0)))
--> ’(a . 1)
M t danh sách là m t tr ng h p đ c bi t c a b đôi nên k t qu tr v có th là m t ph n t không ph i b đôi :
(assq ’a ’((b . 2) (a 1) (c 3) (a . 0))) --> ’(a 1)
Do assq s d ng eq? nên có th phép so sánh không thành công : (assq ’(a) ’((b . 2) (a . 1) (c . 3) ((a) . 0))) --> #f
so sánh nh v y, Scheme còn có hàm t ng t là assv s d ng phép so sánh eqv? và assoc s d ng các phép so sánh equal? :
(assv ’(a) ’((b . 2) (a . 1) (c . 3) ((a) . 0))) --> #f
(assoc ’(a) ’((b . 2) (a . 1) (c . 3) ((a) . 0))) --> ’((a) . 0)
Chú ý r ng các hàm memq, memv, member, assq, assv, và assoc đ u không có d u ch m h i (?) phía sau tên vì chúng tr v không ch các giá tr ki u boolean #f và #t mà còn tr v các giá tr ki u khác c a Scheme.
Sau đây là m t s ví d khác s d ng các hàm assq, assv, và assoc đ tìm ki m trên danh sách : (define e ’ ((a 1) (b 2) (c 3))) (assq ’a e) --> ’(a 1) (assq 'b e) --> ’(b 2) (assq 'd e) --> #f
(assq (list ’a)’(((a)) ((b)) ((c)))) --> #f
(assoc (list ’a) ’(((a)) ((b)) ((c)))) --> ’((a)) (assq 5 ’((2 3) (5 7) (11 13))) --> ’(5 7) (assv 5 ’((2 3) (5 7) (11 13))) --> ’(5 7) 2. S d ng danh sách k t h p
Do assq tr v m t b đôi, ta xây d ng hàm valof đ tìm giá tr k t h p (là cdr) v i khóa c n tìm (là car) :
(define (valof key alist)
(let ((doublet (assq key alist))) (if doublet
(cdr doublet)
#f))) (define L
’((pluto . 9) (jerry . 8) (mickey . 10) (pony . 7) (tom . 8)))
(valof ’tom L) --> 8
(valof ’iago L) -->#f
Xây d ng hàm delkey đ xóa b h t các b đôi có khoá đã cho trong m t danh sách k t h p :
(define (delkey key alist) (cond ((null? alist) '())
((eq? key (caar alist))
(delkey key (cdr alist))) (else (cons (car alist)
(delkey key (cdr alist))))))
(delkey ’b ’((a . 1) (b . 2) (c . 3) (b . 4))) --> ’((a . 1) (c . 3))
(delkey ’d ’((a . 1) (b . 2) (c . 3) (b . 4))) --> ’((a . 1) (b . 2) (c . 3) (b . 4)))
Sau đây ta xây d ng hàm sublis nh n vào hai tham đ i alist và s đ thay th các xu t hi n trong bi u th c s là khóa car trong m i ph n t c a alist b i giá tr t ng ng :
(define (sublis alist s) (cond ((pair? s)
(cons (sublis alist (car s)) (sublis alist (cdr s)))) ((null? s) '())
(else (let ((doublet (assoc s alist))) (if doublet
(cdr doublet) s)))))
(sublis
’((mot . one) (hai . two) (ba . three))) ’(mot + hai = ba))
--> (one + two = three)
b sung vào danh sách k t h p m t ph n t m i, ta xây d ng hàm acons(s1 s2 alist) tr v k t qu là danh sách alist đã đ c thêm vào đ u m t b đôi đ c xây d ng t s1 và s2 :
(define (acons s1 s2 alist) (cons (cons s1 s2) alist))
(acons ’a 1 ’((b . 2) (c . 3) (b . 4))) --> ’((a . 1) (b . 2) (c . 3) (b . 4))
III.4.3.3. D ng quasiquote
Ngoài d ng vi t t t ’(quotation mark) c a phép trích d n quote, trong Scheme còn có d ng vi t t t ` (grave accent) c a phép quasiquote (t m d ch «t ng t trích d n»).
(quasiquote s) --> ‘s ‘s --> ‘s `(a b) --> (a b)
S khác nhau gi a quote và quasiquote liên quan đ n các ph n t có đ t d u ph y tr c trong m t danh sách : nh ng ph n t có d u ph y đ t tr c nh v y đ c tính toán do không còn «b trích d n» n a (unquote). Ví d : (define x 9999) (define y ’(a b c)) `(x y ,x ,y) ; chú ý d u ph y đ t tr c x và y --> ’(x y 9999 (a b c))
(list ’x ’y x y)
--> ’(x y 9999 (a b c))
Nh v y, dùng trích d n quote cho s-bi u th c khi không c n tính giá tr các thành ph n c a nó, còn khi c n tính giá tr , c n đ t m t d u ph y tr c nh ng thành ph n c a s-bi u th c c n tính. Ngh a là : ’s = `s n u không t n t i các d u ph y trong s. ’s ≠ `s n u t n t i các ph n t có d u ph y đ t tr c đ c tính trong s. Ch ng h n : `(x y) --> ’(x y) ’(,a) --> ’((unquote a))
Gi s ta mu n tr v giá tr trích d n quote c a m t s-bi u th c s, ta vi t : (list ’quote y)
--> ’’(a b c) ho c vi t g n h n :
`’,y ; 3 d u liên ti p tr c y --> ’’(a b c)
Ta có th vi t hàm kwote đ th c hi n vi c trên nh sau : (define (kwote s)
`’,s)
(kwote y) --> ’’(a b c)
Trong Scheme còn s d ng hai kí t liên ti p ,@ (d u ph y r i d u commercial at) đ đ t tr c m t s ph n t trong m t s-bi u th c có quasiquote. Khi m t ph n t có hai d u này
đ ng tr c, nó đ c tính giá tr nh ng giá tr ph n t này ph i là m t danh sách và n i dung c a danh sách k t qu s thay th nó.
`(x y ,x ,@y)
--> ’(x y 9999 a b c)
Ký t @ t ng tr ng cho ch a trong append vì ta có th nh n đ c cùng m t k t qu nh append :
(append `(x y ,x) y) --> ’(x y 9999 a b c)
K thu t này th ng đ c dùng khi c n t o sinh các bi u th c hàm.
III.4.4. M t s ví d ng d ng danh sách
1. Tìm ph n t cu i cùng c a danh sách
Xây d ng hàm tr v ph n t cu i cùng c a m t danh sách, tr v #f n u danh sách r ng. ; Cách th nh t
(define (last1 L)
(cond ((null? L) #f) ; danh sách r ng ; danh sách ch có m t ph n t duy nh t ((null? (cdr L)) (car L)) (else (last1 (cdr L))))) (last1 '(1 2 3 -5 -6 0 7 a)) --> a (last1 '()) --> #f
; Cách th hai : s d ng các hàm có s n list-ref và length ; đ l y ph n t cu i cùng n u danh sách khác r ng (define (last2 L) (if (null? L) #f (list-ref L (- (length L) 1)))) (last2 '(1 2 3 -5 -6 0 7 8)) --> 8