CHNG III. KIU D LIU PHC HP
If worms have the power of acquiring some notion,
however rude, of the shape of an object and of their burrows,
as seems to be the case, they deserve to be called intelligent.
Charle R. Darwin (Vegetable Mould, 1881)
iu d liu phc hp trong Scheme gm kiu chui (string), kiu vect (vector), kiu b
đôi (doublet), kiu danh sách. Ngoài ra, Scheme còn mt s kiu d liu phc hp khác.
Kiu d liu th tc (procedure) ch đnh các bin cha giá tr tr v ca hàm. Kiu d liu
cng (port) ch đnh các cng vào-ra tng ng vi các tp và các thit b vào-ra (bàn phím,
màn hình). Cui cùng, tt c các kiu d liu va xét trên đây, k c kiu đn gin, đu đc
Scheme gom li thành mt giuc đc gi là kiu s-biu thc.
K
Sau đây, ta s ln lt trình bày các kiu d liu chui, vect, b đôi và danh sách. Trong
phn trình bày kiu d liu b đôi, chúng ta s nghiên cu khái nim tru tng hoá d liu
(data abstraction).
III.1 Kiu chui
Chui là mt dãy các ký t bt k đc vit gia mt cp du nháy đôi (double-quotes).
Giá tr ca chui chính là bn thân nó. Ví d sau đây là mt chui :
”Cha`o ba.n !”
--> ”Cha`o ba.n !”
Có th đa vào trong chui du nháy đôi, hay du \ (reverse solidus), bng cách đt mt
du \ phía trc, chng hn :
(display ”two \”quotes\” within.”)
--> two ”quotes” within.
Th vin Scheme có hàm string cho phép ghép liên tip các ký t thành mt chui :
(string #\S #\c #\h #\e #\m #\e)
--> ”Scheme”
Ta có th đnh ngha mt bin nhn giá tr kiu chui :
(define greeting ”Scheme ; cha`o ba.n !”)
; Chú ý du ; trong mt chui không có vai trò là du chú thích.
tip cn đn mt ký t ca chui mt v trí bt k, cn s dng hàm string-ref.
Ch s ch v trí ký t là mt s nguyên dng, tính t 0. Ch s hp l ln nht ca chui đã
cho là chiu dài ca chui tr đi 1 :
(string-length greeting)
--> 21
61
62 LP TRÌNH HÀM
(string-ref greeting 0)
--> #\S
(string-ref greeting 20)
--> #\!
(define Str ; s dng hàm ghép liên tip các chui
(string-append ”Chuoi \””
greeting
” \” co do dai 21.”))
(display Str)
--> Chuoi ”Scheme ; cha`o ba.n ! ” co do dai 21.
V t string? dùng đ kim tra kiu chui :
(string? greeting)
--> #t
S dng hàm make-string, ta có th to ra mt chui có đ dài bt k và cha các ký
t bt k :
(define a-5-long-string (make-string 5))
a-5-long-string
--> ”?????”
(define a-5-asterisk-string (make-string 5 #\*))
a-5-asterisk-string
--> ”*****”
Hàm string-set! dùng đ thay th mt ký t trong chui đã
cho ti mt v trí bt k cho bi ch s :
(string-set! a-5-long-string 0 #\c)
a-5-long-string
--> ”c????”
Trên đây ta đã s dng các hàm x lý chui :
(string? Str)
Tr v #t nu str là mt chui, nu không tr v #f.
(string-append str
1
str
2
...)
Tr v chui ghép liên tip các chui str
1
, str
2
.
(make-string k [char])
Tr v mt chui mi có đ dài k. Nu có tham bin char, thì tt c các ký t ca chui
s là char, nu không ni dung ca chui s không đc xác đnh.
(string-length str)
Tr v đ dài ca chui str.
(string-ref str k)
Tr v ký t th k ca chui str. Giá tr ca k phi hp l.
(string-set! string k char)
t giá tr ký t th k ca chui str bi char. Giá tr ca k phi hp l.
Sau đây là mt s hàm x lý chui khác ca Scheme :
Các v t so sánh chui :
(string=? str
1
str
2
)
KIU D LIU PHC HP 63
(string<? str
1
str
2
)
(string>? str
1
str
2
)
(string<=? str
1
str
2
)
(string>=? str
1
str
2
)
tr v #t nu tha mãn quan h th t t vng =, <, >, <=, >= gia các chui (có phân bit
ch hoa ch thng), tr v #f nu không tha mãn. Phép so sánh da trên các v t so sánh
ký t char=?, char<?, char>?, char<=?, và char>=?. Hai chui đc xem nh
tng đng v mt t vng nu chúng có cùng chiu dài và bao gm dãy các ký t ging
nhau tng ng vi char=?.
Các v t sau đây cng cho kt qu tng t nhng phép so sánh chui không phân bit
ch hoa ch thng :
(string-ci=? str
1
str
2
)
(string-ci<? str
1
str
2
)
(string-ci>? str
1
str
2
)
(string-ci<=? str
1
str
2
)
(string-ci>=? str
1
str
2
)
Phép so sánh đc da trên các v t so sánh ký t char-ci=?, char-ci<?, char-
ci>?, char-ci<=?, và char-ci>=?.
Hàm :
(string-copy str)
tr v chui mi là bn sao (copy) ca str :
(string-copy ”abc”)
--> ”abc”
Hàm :
(substring str k l)
tr v bn sao ca str k t ký t có ch s k (k c k) đn ký t có ch s l (không k l).
Các giá tr ca k và l phi hp l.
(substring "Hello, World!" 0 1)
--> ”H”
(substring "Hello, World!" 7 12)
--> ”World”
Hàm substring có th đnh ngha li nh sau :
(define my-substring (s1 m n)
(let ((s2 (make-string (- n m))))
(do ((j 0 (+ j 1)) (i m (+ i 1)))
((= i n) s2)
(string-set! s2 j (string-ref s1 i)))))
(my-substring "Hello, World!" 7 12)
--> ”World”
Trong hàm my-substring s dng cu trúc lp do có cú pháp nh sau :
(do ((var
1
init
1
step
1
)
... )
(test expr ...) command ... )
64 LP TRÌNH HÀM
Cu trúc lp do hot đng tng t nh lnh do trong các ngôn ng mnh lnh : bin
điu khin var
1
nhn giá tr khi đng init
1
có bc thay đi step
1
(nu có) cho mi ln
lp. Khi điu kin lp test cha đc tho mãn (false), biu thc command (nu có) đc
thc hin. Khi test đc tho mãn (true), các biu thc expr đc tính (t trái qua phi)
đng thi là giá tr tr v.
(define L '(1 3 5 7 9))
(do ((L L (cdr L))
(sum 0 (+ sum (car L))))
((null? L) sum))
--> 25
III.2 Kiu d liu vect
Vect là kiu d liu gm mt dãy các phn t tng t kiu chui, nhng mi phn t
có th có kiu bt k, không hoàn toàn ký t. Mi phn t ca vect li có th là mt vect,
t đó to thành vect có nhiu chiu (multidimensional vector). Trong Scheme, các phn t
ca vect đc đt trong mt cp du ngoc (), ngay sát trc cp du ngoc có đt mt du
# (number sign).
’#(999 #\a ”Kiki” (1 2 3) ’x)
--> ’#(999 #\a ”Kiki” (1 2 3) ’x)
S dng hàm vector đ to ra mt vect các s nguyên nh sau :
(vector 1 2 3 4 5)
--> ’#(1 2 3 4 5)
(vector ’x ’y ’z)
--> ’#(x y z)
Hàm make-vector đ to ra mt vect có đ dài n đnh trc :
(make-vector 3)
--> ’#(#{Unspecific} #{Unspecific} #{Unspecific})
(make-vector 3 ”Kiki”)
--> ’#(”Kiki” ”Kiki” ”Kiki”)
Tng t kiu chui, các hàm vector-ref và vector-set! dùng đ tip cn và
thay đi tng ng tng phn t ca vect. V t vector? dùng đ kim tra kiu vect :
(vector? ’#(x y z))
--> #t
S dng cu trúc do, ta to ra mt vect các s t nhiên nh sau :
(do ((v (make-vector 5))
(i 0 (+ i 1)))
((= i 5) v)
(vector-set! vec i i))
--> ’#(0 1 2 3 4)
KIU D LIU PHC HP 65
III.3 Kiu d liu b đôi
III.3.1. Khái nim tru tng hoá d liu
Tru tng hoá th tc (procedure abstraction) là xây dng các ng dng phc tp t
nhng thao tác đn gin, bng cách che du trong chng mc có th nhng chi tit x lý.
Tru tng hoá d liu cng nhm mc đích đnh ngha mt lp d liu phc tp và cách
thao tác trên các d liu đó mà không quan tâm đn cách biu din và cài đt chúng trong
máy vt lý nh th nào. Phng pháp tru tng hoá d liu đc ng dng rng rãi trong
lp trình hng đi tng.
Mt cu trúc d liu tru tng hay kiu d liu tru tng đc đnh ngha, hay đc
đc t (specification) bi 4 thành phn : tên kiu d liu tru tng (types), các đnh ngha
hàm (functions), các điu kin đu (preconditions) nu có và các tiên đ (axioms). Hai thành
phn đu mô t cú pháp v mt cu trúc thuc tính ca kiu d liu tru tng, hai thành
phn sau mô t ng ngha.
Thành phn functions lit kê các khuôn mu hàm (function pattern). Mi khuôn mu
hàm, còn đc gi là mt ký pháp (signature), có dng mt ánh x cho bit kiu ca các tham
đi và ca kt qu nh sau :
Tên-hàm : min-xác-đnh
−
> min-tr
Ngi ta thng phân bit ba loi hàm là hàm kin to (constructor) đ to ra kiu d liu
mi, hàm tip nhn (accessor) đ trích ra các thuc tính và hàm bin đi (transformator) đ
chuyn kiu d liu.
Do các hàm không phi luôn luôn xác đnh vi mi d liu nên ngi ta cng phân bit
các hàm toàn phn (total functions) và các hàm b phn (partial functions). Trong trng hp
các hàm là b phn, ngi ta cn cung cp các điu kin đu là các ràng buc trên min xác
đnh ca hàm. Mt li gi hàm không tuân theo điu kin đu đu dn đn sai sót (chia cho 0,
tính cn bc hai ca mt s âm, v.v...). Chng hn, điu kin đu ca hàm to s hu t
(create-rat n d) là mu s d≠0. Thành phn preconditions có th vng mt nu
không có điu kin đu.
Thành phn axioms mô t các chc nng vn dng hàm, phi đc khai báo rõ ràng,
đy đ và d hiu. Mi vn dng hàm có dng mt mnh đ lôgích hp thc luôn có giá tr
true, ngha là mt hng đúng (tautology). Khi cn s dng các bin, ngi ta s dng t
khoá var đ khai báo trc tên các bin, tng t trong các ngôn ng lp trình mnh lnh
nh C, Pascal...
Chú ý rng kiu tin đnh number cng là mt kiu tru tng : bi vì ta có th bit các
phép toán s hc trên các s nh +, −, *, ... nhng ta li không bit cách biu din chúng
trong b nh. Chng hn sau đây là đc t kiu s t nhiên
Ν
(natual number) :
types nat
functions
0 : -> nat ; 0 là s t nhiên đu tiên, là mt hng (constant)
succ : nat -> nat ; hàm successor ly phn t k tip
+ : nat × nat -> nat
– : nat × nat -> nat
* : nat × nat -> nat
^ : nat × nat -> nat
= : nat × nat -> boolean
66 LP TRÌNH HÀM
axioms var X, Y : nat
X + 0 == X
X + succ(Y) == succ(X + Y)
0 – X ⇔ 0 ; ch làm vic vi các s t nhiên
X – 0 ⇔ X
succ(X) – succ(Y) ⇔ X – Y
X * 0 ⇔ 0
X * succ(Y) ⇔ X + (X * Y)
X ^ 0 ⇔ succ(0)
X ^ succ(Y) ⇔ X * (X ^ Y)
0 = 0 ⇔ true
succ(X) = 0 ⇔ false
0 = succ(X) ⇔ false
succ(X) = succ(Y) ⇔ X = Y
III.3.2. nh ngha b đôi
Trong ngôn ng Scheme, mt b đôi, hay còn đc gi là cp có du chm (dotted pair),
là kiu d liu phc hp tng t bn ghi gm mt cp giá tr nào đó có th t. Có mt du
chm đ phân cách hai giá tr lu gi trong b đôi và các du cách đ phân cách gia du
chm và các giá tr. Phn t (trng) th nht đc gi là car, phn t th hai cdr.
Các tên car và cdr
1
xut hin khi John Mc. Carthy đ xut ngôn ng Lisp chy trên
IBM-704 nm 1956 và đc gi li theo truyn thng.
Gi s any là mt kiu d liu nào đó ca Scheme, ký pháp ca cu trúc b đôi
doublet đc đc t nh sau :
types doublet
functions
cons : any × any -> doublet
car : doublet -> any
cdr : doublet -> any
pair? : any -> boolean
axioms var a, y : any
car(cons(x, y)) = x
cdr(cons(x, y)) = y
T kiu tru tng b đôi, đ to ra b đôi, ngi ta s dng cons :
(cons s
1
s
2
)
--> (<giá tr ca s
1
> . <giá tr ca s
2
>)
Sau đây là mt ví d v mt b đôi : :
(cons ’year 2004) ; gm mt ký hiu và mt s
−-> (year . 2004)
(cons 29 #t) ; gm mt s và mt tr lôgích
−-> (29 . #t)
nh ngha bin có giá tr là mt b đôi :
1
car = content of the address register, cdr = content of the decrement register.
KIU D LIU PHC HP 67
(define x (cons 1 2))
x
--> (1 . 2)
(car x)
--> 1
(cdr x)
--> 2
Trong máy, b đôi đc biên dch nh mt cp con tr tr đn car và cdr.
x
car cdr
1 2
Hình III.1. Biu din các b đôi nh con tr.
Các thành phn car và cdr ca b đôi có vai trò đi xng nhau : hàm car tng ng
vi thành phn đu tiên và hàm cdr tng ng vi thành phn th hai. Ta có quan h tng
quát nh sau :
(car (cons a b)) = <giá tr ca a>
(cdr (cons a b)) = <giá tr ca b>
(car x)
--> 1 x = (1 . 2)
(cdr x)
--> 2
(cdr (car y))
--> 2
V t pair? dùng đ kim tra s-biu thc có phi là mt b đôi hay không ?
(define x ’(1 . 2))
(pair? x)
--> #t ; x = (1 . 2) là mt b đôi
(pair? (cdr x))
--> #f ;(cdr x) = 2 không phi là mt b đôi
V t eq? hay đc dùng đ kim tra hai b đôi có đng nht vi nhau không. Thc cht,
eq? kim tra tính đng nht ca hai con tr.
Ng ngha ca hàm này nh sau :
axioms var x, y : doublet ; a, b : any
(x = cons(a, b)) ∧ (y = x) ⇒ (eq?(x) = eq?(y))
Ví d :
(define x (cons 1 2))
(define y x) ; c x và y cùng tr đn mt b đôi
(eq? x y)
--> #t
Tuy nhiên :
68 LP TRÌNH HÀM
(eq? x (cons 1 2))
--> #f
bi vì x không tr đn b đôi mi đc to ra bi cons !
III.3.3. t bin trên các b đôi
Nh cons, car, cdr, ta đã đnh ngha các b đôi nh là d liu s cp. T đó, ta có th
thay đi b đôi nh các hàm đt bin set-car! và set-cdr!. Các hàm này không to ra
b đôi mi, nhng làm thay đi b đôi đang tn ti. Scheme quy c các hàm làm thay đi
trng thái mt đi tng có tên gi kt thúc bi du chm than !. Ngi ta gi đó là nhng
hàm đt bin (mutator functions). Gi s kiu b đôi đc to ra t hai kiu d liu nào đó
ca Scheme là T1 và T2, ta vit quy c doublet(T1, T2), khi đó các hàm set-car!
và set-cdr! đc b sung vào phn đc t hàm nh sau :
functions
set-car! : doublet(T1, T2) × T1 → any
set-cdr! : doublet(T1, T2) × T2 → any
Ng ngha ca các hàm này nh sau :
axioms var x : doublet(T1, T2) ; a, a1 : T1, b, b1 : T2
(x = cons(a, b)) ∧ (set-car!(x, a1)) ⇒ (car(x) = a1)
(x = cons(a, b)) ∧ (set-cdr!(x, b1)) ⇒ (cdr(x) = b1)
Gi s ta có b đôi :
(define x (cons 1 2))
x
--> (1 . 2)
1
x
2
1 2
x
4
3
(a) (b)
Hnh III.3.1. Biu din các b đôi đt bin.
Trc (a) và sau khi (b) thay đi b đôi
Khi đó nu thc hin :
(set-car! x 3)
(set-cdr! x 4)
thì giá tr ca con tr x không thay đi, c hai trng hp đu cùng tr đn mt b đôi. Tuy
nhiên trong trng hp sau, ni dung ca b đôi đã thay đi, lúc này b đôi cha hai con tr
tr đn hai v trí biu din ln lt 3 và 4. Riêng hai v trí cha ln lt 1 và 2 vn nh c.
Kim tra giá tr ca con tr x :
x
--> (3 . 4)
KIU D LIU PHC HP 69
III.3.4. ng dng b đôi
1. Biu din các s hu t
Scheme s dng b đôi đ biu din kiu d liu danh sách. Trc khi trình bày các phép
x lý trên danh sách, ta có th s dng b đôi đ minh ho mt cách biu din các s hu t,
là mt cp s nguyên (trong Scheme, s hu t có dng tin đnh n/d).
Gi s ta cn xây dng mt tp hp các hàm cho phép x lý các s hu t nh : cng (+),
tr (−), ... Gi s kiu s hu t rational đc đc t nh sau :
types rational
functions
create-rat : integer × integer −> rational
numer : rational −> integer
denom : rational −> integer
=rat : rational × rational −> boolean
var r : rational ; n, n1, n2, d, d1, d2 : integer
preconditions
(r=create-rat(n, d)) ⇒ (numer(r)*d=denom(r) *n) ∧
(denom(r) ≠ 0)
axioms
numer(create-rat(n, d)) = n
denom(create-rat(n, d)) = d
(=rat(create-rat(n1, d1), create-rat(n2, d2)) ⇔ (n1*d2 =
n2*d1)
Hàm create-rat có vai trò to s hu t t t s n và mu s d. Các hàm numer tr
v t s (numerator) và denom tr v mu s (denominator) ca s hu t là các hàm tip
nhn. Hàm =rat là hàm kim tra hay chuyn kiu, kim tra nu hai s hu t là bng nhau.
Khi r là mt s hu t, r=n/d, thì t s ca r là n và mu s là d≠0. Hai s hu t
r1=n1/d1 và r2=n2/d2 bng nhau khi và ch khi n1.d2 =n2/d1.
Chú ý rng phn functions mi ch đnh ngha cú pháp ca hàm, cha xây dng ng
ngha cho nó. Các tên phép toán có mt trong ký pháp là cha đ, mà cn có thêm phn
axioms. Ví d các đnh ngha trong Scheme sau đây tuy đúng đn v mt cú pháp, nhng
hoàn toàn không có ngha s dng đi vi kiu tru tng rational :
(define (create-rat n d) n)
(define (numer r) r)
(define (denom r) r)
(define (=rat r1 r2) (= r1 r2))
(create-rat n d) to s hu t t t s n và mu s d
(numer x) tr v t s (numerator) ca s hu t x
(denom x) tr v mu s (denominator) ca s hu t x
(=rat r1 r2) kim tra nu r1 và r2 đu cùng là mt s hu t
S dng b đôi, ta đnh ngha li bn hàm trên đây theo cách hot đng đc mô t trong
thành phn axioms nh sau :
(define (create-rat n d) (cons n d))
70 LP TRÌNH HÀM
(define (numer r) (car r))
(define (denom r) (cdr r))
(define (=rat r1 r2)
(= (* (car r1) (cdr r2))
(* (cdr r1) (car r2))))
Ví d : Ta áp dng các đnh ngha trên đ to ra các s hu t nh sau :
(define R1 (create-rat 2 3))
(define R2 (create-rat 4 5))
R1
--> ’(2 . 3)
R2
’(4 . 5)
(numer R1)
--> 2
(denom R2)
--> 5
(=rat R1 R2)
--> #f
Ta tip tc đnh ngha các hàm mi trên các s hu t +rat, -rat, *rat, /rat (tng
ng vi các phép toán +, −, *, / ) và hàm chuyn mt s hu t thành s thc rat->real
đ b sung vào kiu d liu tru tng rational nh sau :
functions
+rat : rational × rational −> rational
-rat : rational × rational −> rational
*rat : rational × rational −> rational
/rat : rational × rational −> rational
rat->real : rational −> real
n đây, ngi đc có th t mình vit b sung phn ng ngha cho các hàm mi. Các
hàm này đc đnh ngha trong Scheme nh sau :
(define (+rat x y)
(create-rat (+ (* (numer x) (denom y)) (* (denom x) (numer y)))
(* (denom x) (denom y))))
(define (-rat x y)
(create-rat (- (* (numer x) (denom y)) (* (denom x) (numer y)))
(* (denom x) (denom y))))
(define (*rat x y)
(create-rat (* (numer x) (numer y))
(* (denom x) (denom y))))
(define (/rat x y)
(create-rat (* (numer x) (denom y))
(* (denom x) (numer y))))
; chuyn mt s hu t thành s thc :
(define (rat->real r) (/ (numer r) (denom r)))
KIU D LIU PHC HP 71
(+rat R1 R2)
--> (22 . 15)
(rat->real R1)
--> 0.666666666666667
(rat->real R2)
--> 0.8
Ví d bài toán x lý s hu t dn đn nhiu mc lp trình có đ tru tng gim dn nh sau :
− X lý các s hu t
− X lý các phép toán trên các s hu t : +rat, -rat, *rat, /rat, =rat
− X lý s khi trên các s hu t : create-rat, numer, denom
− X lý s khi trên các b đôi : cons, car, cdr
Khi thit k chng trình đnh hng cu trúc trong phng pháp tinh ch tng bc,
ngi lp trình quan tâm đn các mc tru tng cao hn, bng cách u tiên chc nng x lý
nh th nào, mà chn chm li (trì hoãn) cách biu din các cu trúc d liu tng ng. i
vi các s hu t, ta có th biu din chúng di dng phân s rút gn nh sau :
(define (create-rat n d)
(define g (gcd (abs n) (abs d)))
; Chú ý hàm th vin gcd (greatest common divisor)
; ch tính c s chung ln nht cho các s nguyên dng
(cons (quotient n g) ( quotient d g)))
(define (numer r) (car r))
(define (denom r) (cdr r))
(define (=rat x y)
(and (= (car x) (car y))
(= (cdr x) (cdr y))))
(create-rat 6 14)
--> (3 . 7)
(gcd 12 3)
--> 3
Các đnh ngha hàm trên đây không làm thay đi tính cht s hc ca các s hu t, cng
nh không làm thay đi mc đ tru tng ca bài toán. Hai cách đnh ngha th tc
create-rat va trình bày là đáng tin cy vì trc khi gi chúng, ta đã gi thit rng điu
kin đu (d
≠
0) đã đc tho mãn.
D phòng và khc phc nhng sai sót có th xy ra, trong khi lp trình hay trong quá
trình khai thác ca ngi s dng, bng cách kim tra điu kin đu, đc gi là phng
pháp lp trình d phòng (defensive programming). Phng pháp lp trình này làm cho
chng trình tr nên đáng tin cy và d thuyt phc ngi s dng.
Ta vit li th tc create-rat nh sau :
(define (create-rat n d)
(if (zero? d) ; kim tra điu kin đu
(display ”ERROR: *** Mu s bng 0 !”)
(cons n d)))
(create-rat 4 0)
--> ERROR: *** Mu s bng 0 !
72 LP TRÌNH HÀM
2. Biu din hình ch nht phng
Gi s cn mô hình hóa các hình ch nht trong ta đ phng xoy có các cnh song song
vi các trc to đ, ta có th có nhiu phng pháp mô t hình ch nht qua ta đ nh sau :
1. Ta đ ca hai đnh đi din.
2. Ta đ tâm đim và hai đ dài cnh.
3. Ta đ ca đnh di cùng bên trái và hai đ dài cnh.
Ta có th chn phng pháp th 3. Gi s x, y là ta đ ca đnh A (xem hình v), y là đ
dài ca cnh song song vi 0x và H là đ dài ca cnh song song vi 0y. Ta cn đnh ngha
mt hàm đ ánh x mt hình ch nht thành mt đi tng Scheme, gi là biu din trong
(internal representation) ca hình ch nht.
Hình III.2. Biu din các thành phn ca mt hình ch nht.
n đây, ta có th ngh đn nhiu cách biu din nh sau :
A
y
0 x
L
H
• S dng b đôi ((x y) . (L H)),
• hoc s dng b đôi ((x . y) . (L . H)),
• hoc s dng mt danh sách gm các thành phn (x y L H) (cu trúc danh sách s
đc trình bày trong mc sau),
• v. v...
Tm thi ta cha chn cách biu din hình ch nht (da theo quan đim ca phng
pháp lp trình cu trúc : c gng trì hoãn vic khai báo d liu trong chng mc có th). Ta
xây dng hàm cons-rectangle đ to mi mt hình ch nht theo các thành phn x, y, y,
H. T mt cách biu din trong nào đó ca hình ch nht, ta cn xây dng các hàm tip cn
đn các thành phn này là value-x, value-y, value-L, value-H. Các hàm này thc
hin các phép toán trên hình ch nht mt cách đc lp vi biu din trong đã cho.
u tiên, ta cn xây dng v t (in? xo yo R) kim tra nu đim M có to đ x
o
, y
o
có nm bên trong (hay nm trên các cnh) ca mt hình ch nht R đã cho. Ta thy đim M
phi có ta đ ln hn đnh di cùng bên trái và thp hn đnh cui cùng bên phi ca R. Ta
có chng trình nh sau :
(define (in? xo yo R)
(let ((x1 (value-x R))
((y1 (value-y R))
((L (value-L R))
((H (value-H R)))
(and (<= x1 xo) (<= y1 yo)
(<= xo (+ x1 L))
(<= yo (+ y1 H)))))
KIU D LIU PHC HP 73
Bây gi xây dng v t (inclus? R1 r2) đ kim tra hình ch nht R1 có nm bên
trong hình ch nht R2 không ? Nu đúng, tr v #t. Mun vy, ch cn R2 cha hai đnh đi
din ca R1. Ta có chng trình nh sau :
(define (inclus? R1 R2)
(let ((x1 (value-x R1))
((y1 (value-y R1))
((L1 (value-L R1))
((H1 (value-H R1)))
(and (in? x1 y1 R2)
(in? (+ x1 L2) (+ y1 H1) R2))))
tnh tin (translate) mt hình ch nht R theo mt véct V có các thành phn a, b, ch
cn tnh tin đnh di cùng bên trái ca nó. Ta có hàm tnh tin nh sau :
(define (translate R a b)
(let ((x (value-x R))
((y (value-y R))
((L (value-L R))
((H (value-H R)))
(cons-rectangle (+ x a) (+ y b) L H)))
Hình III.3. Tnh tin mt hình ch nht.
Nh đã trình bày, vic x lý trên các hình ch nht không ph thuc vào cách biu din
d liu. Khi cn thay đi cách biu din, ch cn vit li các hàm to mi hình ch nht và
các hàm tip cn đn các thành phn, mà không cn thay đi các hàm in?, inclus?, và
translate.
Chng hn ta có th biu din hình ch nht bi b đôi :
((x . y) . (L . H))
Khi đó, cách biu din trong ca hình ch nht đc xây dng nh sau :
(define (cons-rectangle x y L H)
(cons (cons x y) (cons L H)))
(define (value-x R) (car (car R)))
(define (value-y R) (cdr (car R)))
(define (value-L R) (car (cdr R)))
(define (value-H R) (cdr (cdr R)))
Ví d :
(define R (cons-rectangle 1 2 3 4))
R
--> '((1 . 2) 3 . 4) ; tng đng vi '((1 . 2) . (3 . 4))
b
V
y
0 x a
L
H
74 LP TRÌNH HÀM
(value-L R)
--> 3
(value-H R)
--> 4
III.4 Kiu d liu danh sách
III.4.1. Khái nim danh sách
Khi gii mt s bài toán, ngi ta thng phi nhóm nhiu d liu thành mt d liu t
hp duy nht. nhóm các ngày trong tun là Monday, Tuesday, Wednesday, Thursday,
Friday, Saturday và Sunday thành mt t hp d liu, ngi ta s dng cu trúc danh sách có
dng mt s-biu thc nh sau :
(mon tue wed thu fri sat sun)
Tuy nhiên, nu đnh ngha :
(define week-list)
(mon tue wed thu fri sat sun))
--> *** ERROR-unbound variable: mon
thì ta gp li s sai sót v các ký t. Scheme xem danh sách này nh là mt li gi hàm có
tên mon vi các tham đi là tue, ... sun và, nh đã thy, ta cha h đnh ngha hàm mon
(!). đnh ngha danh sách trên, Scheme s dng phép toán trích dn :
(define week-list
’(mon tue wed thu fri sat sun))
Khi đó ta có week-list là bin có giá tr là mt danh sách các ký t :
week-list
--> (mon tue wed thu fri sat sun))
x lý thun tin, ngi ta s dng bin L đ ch đnh mt danh sách, ta có :
(define L ’(1 2 3 4 5))
L
--> ’(1 2 3 4 5)
Có th s dng các danh sách phc tp hn đ biu din các biu thc. Có th xây dng
các biu thc s hc, biu thc cha các d liu ký hiu, hay cha các danh sách lng nhau.
Mt biu thc khi đc trích dn đc gi là mt biu thc trc kin (literal expression) vì
rng giá tr ca nó chính là vn bn (text) to ra nó. Biu thc trc kin có vai trò là mt
hng :
Cho trc mt danh sách, khi ta nói đ dài hay s phn t ca danh sách (length) là s
phn t ca danh sách đó. Sau đây là mt s ví d :
’(1 2 2 3) ; là mt danh sách gm 4 s nguyên
--> ’(1 2 2 3)
’(one + two = three) ; là mt danh sách gm 5 ký hiu tu ý
--> (one + two = three)
KIU D LIU PHC HP 75
’() ; danh sách rng (empty list) không có phn t nào
--> ’()
’(* 4 5) ; không phi là mt biu thc tính đc
--> (* 4 5)
Nu ta cn biu din các thông tin v mt trang lch công tác trong ngày, ta có th s
dng mt danh sách dng :
(define L-congviec-10-10-2000
’(thu-hai
(sang
(9h doc-thu)
(9h-30 chuan-bi-len-lop)
(10h len-lop)
(12h an-trua))
(chieu
(14h tham-gia-chuyen-de)
(16h den-cuoc-hen))))
Kiu d liu danh sách cn có th thut đ có th đc vào và in ra. Scheme cho phép xây
dng các danh sách không thun nht (heterogeneous list) v kiu. Chng hn :
(define L ’(1 (2 3) x "bonjour"))
(list-ref L 0)
--> 1
(list-ref L 3)
--> "bonjour"
Tr trng hp ngc li, đây ta ch nên x lý các danh sách thun nht (homogeneous
list) là danh sách ch cha cùng mt kiu d liu T đã cho : cha toàn các s, hoc ch cha
toàn các ký hiu, v.v... Th t ca các phn t xut hin trong mt danh sách là có ý ngha.
Mt trong nhng đc trng ca các chng trình h Lisp là có cùng bn cht vi d liu.
Chng hn, đnh ngha hàm (define...) cng là mt danh sách. Ngi ta gi mt danh
sách con (sub-list) là mt phn t ca danh sách đã cho. Phn t này có th là mt danh
sách, hay là mt danh sách con ca mt danh sách con.
Ví d, danh sách :
’(a (b c) () ((c)) d)
có các danh sách con là : (b c), (), ((c)) và (c). Mt danh sách không có danh sách
con đc gi là mt danh sách phng (plate list).
Ngi ta nói rng mt phn t là mc mt (first level) ca mt danh sách nu phn t
này không trong mt danh sách con.
Các phn t mc mt ca danh sách va nêu trên đây là :
a, (b c), (), ((c)), c
Ngi ta gi các phn t mc th n là nhng phn t mc mt ca mt danh sách con
đc đt mc n-1.
sâu (depth) ca mt danh sách là mc ca mt phn t mc cao nht. Chng hn
danh sách trên đây có đ sâu là 3.
dài (length) ca mt danh sách là s các phn t mc th nht, đc tính nh hàm
tin đnh length ca Scheme :
76 LP TRÌNH HÀM
(length ’((a b) (c d) () (e)))
--> 4
( a ( b c ) () ( ( c ) ) d ) danh sách đã cho
a ( b c ) () ( ( c ) ) d các phn t mc 1
b c ( c ) các phn t mc 2
c các phn t mc 3
Hình III.4. Biu din các mc ca mt danh sách phc hp.
nh ngha kiu d liu tru tng danh sách
Có th có nhiu cách khác nhau đ đnh ngha mt danh sách. Sau đây là đc t kiu d
liu tru tng danh sách list(T) s dng các phép toán c bn cons, car và cdr, trong đó,
T là kiu phn t ca danh sách :
types list(T)
functions
() : list(T)
null? : list(T) -> boolean
pair? : list(T) -> boolean
cons : T × list(T) -> list(T)
car : list(T) -> T
cdr : list(T) -> list(T)
preconditions
car(x) ch xác đnh vi x ≠ 0
cdr(x) ch xác đnh vi x ≠ 0
axioms var L : list(T), x : T
car(cons (x, L)) = x
cdr(cons (x, L)) = L
null?(()) = true
null?(cons (x, L)) = false
pair?(()) = false
pair?(cons (x, L)) = true
III.4.2. ng dng danh sách
III.4.2.1. Các phép toán c bn cons, list, car và cdr
Scheme s dng b xây cons đ to mt danh sách :
(cons s L)
tr v mt danh mi bng cách thêm giá tr ca s trc danh sách L.
(cons ’a ’())
--> (a)
(cons (+ 5 8) ’(a b))
--> (13 a b)
Scheme có th nhn bit mt danh sách và đa ra theo cách riêng :
KIU D LIU PHC HP 77
(cons 1 2)
--> (1 . 2)
(cons 1 ’()) ; chú ý du quote do danh sách rng làm tham đi
--> (1) ; không phi (1 . ())
(cons 1 (cons 2 (cons 3 ())))
-> (1 2 3) ; không phi (1 . (2 . (3 . ())))
xây dng danh sách, Scheme thng s dng hàm list. Quan h gia b xây cons
và cu trúc list đc tóm tt bi đng thc sau :
(list <e
1
> ... <e
N
>) =
(cons <e
1
> (cons <e
2
> ... (cons <e
N
> ’())...))
Ví d :
(list (cons 1 2) (cons 3 4))
--> ((1 . 2) (3 . 4)) ; (1 . 2) và (3 . 4) là các b đôi
Hàm list nhn mt s tham đi tùy ý là các s-biu thc đ tr v mt danh sách mi t
hp t các giá tr ca các tham đi đó. Ví d :
(list 1 2 3)
--> (1 2 3)
(list ’(a b) ’(c d) ’(c d) ’() ’((e)))
--> ((ab) (cd) () (e))
Danh sách sau đây gm 3 phn t, phn t th nht là du + :
’(+ 1 2)
--> (+ 1 2)
Chú ý kiu d liu ca các phn t danh sách :
(list a b c)
--> Error: undefined variable a
(list ’a ’b ’c)
’ (a b c)
Các dng car và cdr dùng đ tip cn đn các phn t ca mt danh sách khác rng.
Hàm (car L) tr v phn t đu tiên ca L
(car ’(a b c))
--> a
(car ’(1 2 3)) == (car (quote 1 2 3))
--> 1
(car ’(+ 1 2))
--> +
(car ’’1)
--> quote
Hàm (cdr L) tr v danh sách L đã ly đi phn t đu tiên
(cdr ’(a b c))
--> (b c)
Ta cng có các đng thc sau :
(car (cons s L)) =
-
s
(cdr (cons s L)) = L
-
78 LP TRÌNH HÀM
Vi quy c du gch trên tên ch đnh giá tr ca mt biu thc. Trong Scheme, car hay cdr
ca mt danh sách rng đu dn đn mt li sai, trong khi trong các ngôn ng phát trin t Lisp thì
thng có kt qu tr v là mt danh sách rng. Nu mun thay đi tên các hàm car và cdr
này, ch cn đnh ngha các hàm đng ngha head và tail nh sau :
(define head car)
(define tail cdr)
Tuy nhiên, không nên đnh ngha nh vy, vì rng s gp khó khn khi công vic lp trình
có s tham gia ca nhiu ngi. Bng cách t hp các hàm car hoc cdr, ta có th tip cn
đn bt k mt phn t nào ca mt danh sách :
(car (cdr week-list))
--> mardi
(car (cdr (cdr week-list))))
--> mercredi
Cách vit t hp trên đây s làm ngi đc khó theo dõi. Chính vì vy ngi ta đa ra
mt h thng vit tt đ ch đnh s t hp ti đa là bn car hay cdr. Scheme quy c :
cx
1
x
2
x
3
x
4
r vi x
j
là mt a hoc mt d, j =1..4
là dãy «hàm» ghép : cx
1
r
°
cx
2
r
°
cx
3
r
°
cx
4
r
Scheme có tt c 28 hàm ghép nh vy. Ví d :.
(caadr ’(a (b) c d e)) ; là hàm car
°
car
°
cdr
--> b
(cddddr ’(a (b) c d e)) ; là hàm cdr
°
cdr
°
cdr
°
cdr
--> (e)
(cddddr week-list)
--> (vendredi samedi dimanche)
Sau đây, ta có th s dng hai s đ tng quát đ đnh ngha các hàm x lý danh sách :
S đ đ quy tuyn tính :
(define (<fname> <arg>)
(if (null? <arg>)
<init-val>
(<func> (car <arg>) (<fname> (cdr <arg>)))))
S đ lp :
(define (<fname> <arg>)
(define (<i-name> <arg> <result>)
(if (null? <arg>)
<result>
(<i-name> (cdr <arg>)
(<func> (car <arg>) <result>))))
(<i-name> <arg> <init-val>))
V t null? ca Scheme cho phép kim tra mt danh sách rng.
(null? '())
--> #t
KIU D LIU PHC HP 79
III.4.2.2. Các hàm x lý danh sách
Th vin ca Scheme có sn nhiu hàm x lý danh sách. Sau đây ta đc t b sung mt s
hàm x lý danh sách thông dng vào kiu d liu tru tng list(T) nh sau :
functions
length : list (T) -> Integer
append : ist(T) × list(T) -> list(T)
reverse : list(T) -> list(T)
1. Các hàm length, append và reverse
Hàm length tr v đ dài ca danh sách đc đnh ngha nh sau :
(define (length L)
(if (null? L)
0
(+ 1 (length (cdr L)))))
Hàm length có chi phí tuyn tính. Ví d :
(length (list 1 2 3))
--> 3
(= (length (list 1 2 3 4)) 0)
--> #f
Các danh sách rng có th đc to ra bi hàm (list) không có tham đi, hoc s
dng trc kin ’(). Ví d :
(= (length (list)) 0)
--> #t
(= (length ’()) 0)
--> #t
(null? (list 1 2 3 4))
--> #f
(null? (list))
--> #t
(null? ’())
--> #t
Scheme không tha nhn dng ’() là mt biu thc, mà xem ’() là mt giá tr. Vì
vy, đ s dng danh sách rng, ta cn trích dn :
’()
--> ‘()
Hàm append nhn mt s tham đi tùy ý, mi tham đi có giá tr là mt danh sách đ
tr v mt danh sách mi, là ghép (concatenation) tt c các danh sách tham đi này. Hàm
append đc đnh ngha nh sau :
(define (append L1 L2)
(if (null ? L1)
L2
(cons (car L1) (append (cdr L1) L2))))
Hàm append có chi phí tuyn tính theo L1. Ví d :
80 LP TRÌNH HÀM
(append (list 1 2 3) (list 4 5 6 7))
--> (1 2 3 4 5 6 7)
(append ’(a b) ’(c d) ’() ’((e)))
--> (a b c e (e))
Không nên nhm ln cách s dng ca các hàm cons, list, và append :
(cons ’(a b) ’(c d))
--> ((a b) (c d))
(list ’(a b) ’(c d))
--> ((a b) (c d))
(append ’(a b) ’(c d))
--> (a b c d)
Hàm reverse tr v danh sách nghch đo (reverse list) ca tham đi, đc đnh ngha
đ quy nh sau :
(define (reverse L)
(if (null? L)
’() ; hoc (list)
(append (reverse (cdr L)) (list (car L))))
Chi phí ca hàm reverse là tuyn tính theo L. Ví d :
(reverse ’(dog cat cock duck pig))
’(pig duck cock cat dog)
(reverse (list 1 2 3))
--> (3 2 1)
(reverse (append ’(a b) ’(c d)))
--> '(d c b a)
Chú ý ngi lp trình thng gp li sai v kiu gia car, cdr và list. Nên thng
xuyên thc hin vic phân tích kiu. Chng hn nu dòng cui trong đnh ngha hàm
reverse vit li là :
(append (reverse (cdr L)) (car L)))) ; quên đt tin t list
thì s gây ra li sai vì (car L) là mt phn t, không phi là mt danh sách. Ta có th
đnh ngha li hàm reverse nh phép lp nh sau :
(define (reverse L)
(define (rev-iter L R)
(if (null? L)
R
(rev-iter (cdr L) (cons (car L) R))))
(rev-iter L ’())) ; hoc (list)
(reverse (list 1 2 3 4))
--> (4 3 2 1)
Chi phí ca hàm reverse vn tu thuc tuyn tính vào L.
2. Các hàm tham chiu danh sách
(list-ref L n)
(list-tail L n)
(list? L)
KIU D LIU PHC HP 81
Hàm (list-ref L n) tr v phn t th n ca danh sách L, vi quy c rng trong
mt danh sách, các phn t đc đánh s t 0.
(define (list-ref L n)
(if (zero? n)
(car L)
(list-ref (cdr L) (- n 1))))
(list-ref '(a b c d e) 3)
--> d
Hàm (list-tail L n) tr v phn t còn li ca danh sách L sau khi b đi n phn t
đu tiên :
(define (list-tail L n)
(if (zero? n)
L
(list-tail (cdr L) (- n 1))))
(list-tail ’(a b c) 0)
--> ’(a b c)
(list-tail ’(a b c) 2)
--> ’(c)
(list-tail ’(a b c) 3)
--> ’()
(list-tail ’(a b c . d) 2)
--> ’(c . d)
(list-tail ’(a b c . d) 3)
--> d
(define week-end (list-tail week-list 5))
week-end
--> (samedi dimanche)
V t (list? L) kim tra L có phi là mt danh sách không :
(list? ’())
--> #t
(list? ’(a b c))
--> #t
(list? ’a)
--> #f
(list? ’(3 . 4))
--> #f
(list? 3)
--> #f
3. Các hàm chuyn đi kiu
(string->list str)
(list->string L)
(string->number str [radix])
82 LP TRÌNH HÀM
Hàm (string->list str) chuyn mt chui thành mt danh sách. Hàm này có th
đc đnh ngha nh sau :
(define (tring->list str)
(do ((i (- (string-length str) 1) (- i 1))
(ls ’() (cons (string-ref str i) ls)))
((< i 0) ls)))
(string->list ””)
--> ’()
(string->list ”abc”)
--> ’(#\a #\b #\c)
(apply char<? (string->list ”abc”))
--> #t
(map char-upcase (string->list ”abc”))
--> ’(#\A #\B #\C)
(map char-downcase (string->list ”ABC”))
--> ’ (#\a #\b #\c)
Chú ý hàm (char-upcase ch) chuyn mt ch thng thành ch hoa,
còn hàm (char-downcase ch) chuyn mt ch hoa thành ch thng.
Hàm (list->string L) chuyn mt danh sách thành mt chui. Hàm này có th
đc đnh ngha nh sau :
(define (list->string ls)
(let ((s (make-string (length ls))))
(do ((ls ls (cdr ls)) (i 0 (+ i 1)))
((null? ls) s)
(string-set! s i (car ls)))))
(list->string ’())
--> ””
(list->string ’(#\a #\b #\c))
--> ”abc”
(list->string
(map char-upcase
(string->list ”abc”)))
--> ”ABC”
Hàm (string->number str [radix]) chuyn mt chui str thành mt s h
10 (mc nhiên). Nu có thêm thành phn radix là mt s nguyên ch c s h đm 2, 8, 10
hay 16 thì s trong h đm tng ng s đc đi sang h 10 :
(string->number ”9999”)
--> 100
(string->number ”1e3”)
--> 1000.0
(string->number "1ABC" 16)
--> 6844
KIU D LIU PHC HP 83
4. Các hàm so sánh danh sách
so sánh các danh sách, ngi ta dùng v t equal? :
(equal? ’(a b 3) (list ’a ’b (+ 1 2)))
--> #t
(equal? ’(()) ’())
--> #t
Ta đã gp có các v t xác đnh kiu d liu danh sách ca mt s-biu thc :
(list? s)--> #t nu giá tr ca biu thc s là mt danh sách bt k
(null? s)--> #t nu giá tr ca biu thc s là mt danh sách rng
(pair? s)--> #t nu giá tr ca biu thc s là mt danh sách khác rng.
ôi khi ngi ta cn kim tra nu mt s-biu thc không phi là mt danh sách và kim
tra nu mt danh sách có phi đc rút gn thành mt phn t duy nht không. Ta thêm các
v t sau đây vào th vin các tin ích vn cha có trong th vin tin đnh ca Scheme :
V t atom? kim tra nu mt s-biu thc không phi là mt danh sách :
(define (atom? s)
(not (pair? s)))
V t singleton? kim tra mt danh sách đc rút gn thành mt phn t duy nht :
(define (singleton L)
(null? (cdr L)))
Ngi ta cng thng phi kim tra nu mt đi tng là mt phn t mc th nht ca
mt danh sách. Mun vy, ngi ta s dng mt hàm tin đnh (member S L). Hàm này
tr li mt phn ca danh sách L cha phn đu tiên (first ocurrence) ca giá tr s, hoc tr v
#f nu danh sách L không có. S so sánh đc thc hin nh kim tra equal? :
(member ’(b) ’(a b c d))
--> #f
(member ’(b) ’(a b c (b) d))
--> ((b) d)
Scheme xem các giá tr bng nhau các mc đ chính xác khác nhau. V t equal? so
sánh các cu trúc nhng không phi là s so sánh tên ca các đi tng. Tên ca các đi
tng đc kim tra nh v t eq? và tn ti mt v t so sánh ít cht ch hn là eqv?.
Nh vy, đ so sánh các danh sách hoc các chui, ta s dung equal?, đ so sánh các
s, s dng = và đ so sánh các ký hiu, s dng eq?. Scheme có ba hàm so sánh hot đng
nh sau :
member s dng phép kim tra equal?
memv s dng phép kim tra eqv?
memq s dng phép kim tra eq?
Sau đây là đnh ngha ca hàm member :
(define (memq s L)
(cond
((null? L) #f)
((eq? (car L) s) L)
(else (memq s (cdr L)))))
Ví d :