Tải bản đầy đủ (.pdf) (90 trang)

Tài liệu Kiểu dữ liệu phức hợp_chương 3 doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (867.51 KB, 90 trang )








CHNG III. KIU D LIU PHC HP
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)
iu d liu phc hp trong Scheme gm kiu chui (string), kiu vect (vector), kiu b
đôi (doublet), kiu danh sách. Ngoài ra, Scheme còn mt s kiu d liu phc hp khác.
Kiu d liu th tc (procedure) ch đnh các bin cha giá tr tr v ca hàm. Kiu d liu
cng (port) ch đnh các cng vào-ra tng ng vi các tp và các thit b vào-ra (bàn phím,
màn hình). Cui cùng, tt c các kiu d liu va xét trên đây, k c kiu đn gin, đu đc
Scheme gom li thành mt giuc đc gi là kiu s-biu thc.
K
Sau đây, ta s ln lt trình bày các kiu d liu chui, vect, b đôi và danh sách. Trong
phn trình bày kiu d liu b đôi, chúng ta s nghiên cu khái nim tru tng hoá d liu
(data abstraction).
III.1 Kiu chui
Chui là mt dãy các ký t bt k đc vit gia mt cp du nháy đôi (double-quotes).
Giá tr ca chui chính là bn thân nó. Ví d sau đây là mt chui :
”Cha`o ba.n !”
--> ”Cha`o ba.n !”
Có th đa vào trong chui du nháy đôi, hay du \ (reverse solidus), bng cách đt mt
du \ phía trc, chng hn :
(display ”two \”quotes\” within.”)
--> two ”quotes” within.


Th vin Scheme có hàm string cho phép ghép liên tip các ký t thành mt chui :
(string #\S #\c #\h #\e #\m #\e)
--> ”Scheme”
Ta có th đnh ngha mt bin nhn giá tr kiu chui :
(define greeting ”Scheme ; cha`o ba.n !”)
; Chú ý du ; trong mt chui không có vai trò là du chú thích.
 tip cn đn mt ký t ca chui  mt v trí bt k, cn s dng hàm string-ref.
Ch s ch v trí ký t là mt s nguyên dng, tính t 0. Ch s hp l ln nht ca chui đã
cho là chiu dài ca chui tr đi 1 :
(string-length greeting)
--> 21
61
62 LP TRÌNH HÀM
(string-ref greeting 0)
--> #\S
(string-ref greeting 20)
--> #\!
(define Str ; s dng hàm ghép liên tip các chui
(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 đ kim tra kiu chui :
(string? greeting)
--> #t
S dng hàm make-string, ta có th to ra mt chui có đ dài bt k và cha các ký
t bt 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 mt ký t trong chui đã
cho ti mt v trí bt k cho bi ch s :
(string-set! a-5-long-string 0 #\c)
a-5-long-string
--> ”c????”
Trên đây ta đã s dng các hàm x lý chui :
(string? Str)
Tr v #t nu str là mt chui, nu không tr v #f.
(string-append str
1
str
2
...)
Tr v chui ghép liên tip các chui str
1
, str
2
.
(make-string k [char])
Tr v mt chui mi có đ dài k. Nu có tham bin char, thì tt c các ký t ca chui
s là char, nu không ni dung ca chui s không đc xác đnh.
(string-length str)
Tr v đ dài ca chui str.
(string-ref str k)
Tr v ký t th k ca chui str. Giá tr ca k phi hp l.
(string-set! string k char)

t giá tr ký t th k ca chui str bi char. Giá tr ca k phi hp l.
Sau đây là mt s hàm x lý chui khác ca Scheme :
Các v t so sánh chui :
(string=? str
1
str
2
)
KIU D LIU PHC HP 63
(string<? str
1
str
2
)
(string>? str
1
str
2
)
(string<=? str
1
str
2
)
(string>=? str
1
str
2
)
tr v #t nu tha mãn quan h th t t vng =, <, >, <=, >= gia các chui (có phân bit

ch hoa ch thng), tr v #f nu không tha mãn. Phép so sánh da trên các v t so sánh
ký t char=?, char<?, char>?, char<=?, và char>=?. Hai chui đc xem nh
tng đng v mt t vng nu chúng có cùng chiu dài và bao gm dãy các ký t ging
nhau tng ng vi char=?.
Các v t sau đây cng cho kt qu tng t nhng phép so sánh chui không phân bit
ch hoa ch thng :
(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 da 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 chui mi là bn sao (copy) ca str :
(string-copy ”abc”)
--> ”abc”
Hàm :
(substring str k l)
tr v bn sao ca 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 ca k và l phi hp l.
(substring "Hello, World!" 0 1)
--> ”H”
(substring "Hello, World!" 7 12)
--> ”World”
Hàm substring có th đnh ngha li 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 dng cu trúc lp do có cú pháp nh sau :
(do ((var
1
init
1
step

1
)
... )
(test expr ...) command ... )
64 LP TRÌNH HÀM
Cu trúc lp do hot đng tng t nh lnh do trong các ngôn ng mnh lnh : bin
điu khin var
1
nhn giá tr khi đng init
1
có bc thay đi step
1
(nu có) cho mi ln
lp. Khi điu kin lp test cha đc tho mãn (false), biu thc command (nu có) đc
thc hin. Khi test đc tho mãn (true), các biu thc expr đc tính (t trái qua phi)
đng thi 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 Kiu d liu vect
Vect là kiu d liu gm mt dãy các phn t tng t kiu chui, nhng mi phn t
có th có kiu bt k, không hoàn toàn ký t. Mi phn t ca vect li có th là mt vect,
t đó to thành vect có nhiu chiu (multidimensional vector). Trong Scheme, các phn t
ca vect đc đt trong mt cp du ngoc (), ngay sát trc cp du ngoc có đt mt du
# (number sign).
’#(999 #\a ”Kiki” (1 2 3) ’x)
--> ’#(999 #\a ”Kiki” (1 2 3) ’x)
S dng hàm vector đ to ra mt 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 đ to ra mt vect có đ dài n đnh trc :
(make-vector 3)
--> ’#(#{Unspecific} #{Unspecific} #{Unspecific})
(make-vector 3 ”Kiki”)
--> ’#(”Kiki” ”Kiki” ”Kiki”)
Tng t kiu chui, các hàm vector-ref và vector-set! dùng đ tip cn và
thay đi tng ng tng phn t ca vect. V t vector? dùng đ kim tra kiu vect :
(vector? ’#(x y z))
--> #t
S dng cu trúc do, ta to ra mt 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)

KIU D LIU PHC HP 65
III.3 Kiu d liu b đôi
III.3.1. Khái nim tru tng hoá d liu
Tru tng hoá th tc (procedure abstraction) là xây dng các ng dng phc tp t
nhng thao tác đn gin, bng cách che du trong chng mc có th nhng chi tit x lý.
Tru tng hoá d liu cng nhm mc đích đnh ngha mt lp d liu phc tp và cách
thao tác trên các d liu đó mà không quan tâm đn cách biu din và cài đt chúng trong
máy vt lý nh th nào. Phng pháp tru tng hoá d liu đc ng dng rng rãi trong
lp trình hng đi tng.
Mt cu trúc d liu tru tng hay kiu d liu tru tng đc đnh ngha, hay đc

đc t (specification) bi 4 thành phn : tên kiu d liu tru tng (types), các đnh ngha
hàm (functions), các điu kin đu (preconditions) nu có và các tiên đ (axioms). Hai thành
phn đu mô t cú pháp v mt cu trúc thuc tính ca kiu d liu tru tng, hai thành
phn sau mô t ng ngha.
Thành phn functions lit kê các khuôn mu hàm (function pattern). Mi khuôn mu
hàm, còn đc gi là mt ký pháp (signature), có dng mt ánh x cho bit kiu ca các tham
đi và ca kt qu nh sau :
Tên-hàm : min-xác-đnh

> min-tr
Ngi ta thng phân bit ba loi hàm là hàm kin to (constructor) đ to ra kiu d liu
mi, hàm tip nhn (accessor) đ trích ra các thuc tính và hàm bin đi (transformator) đ
chuyn kiu d liu.
Do các hàm không phi luôn luôn xác đnh vi mi d liu nên ngi ta cng phân bit
các hàm toàn phn (total functions) và các hàm b phn (partial functions). Trong trng hp
các hàm là b phn, ngi ta cn cung cp các điu kin đu là các ràng buc trên min xác
đnh ca hàm. Mt li gi hàm không tuân theo điu kin đu đu dn đn sai sót (chia cho 0,
tính cn bc hai ca mt s âm, v.v...). Chng hn, điu kin đu ca hàm to s hu t
(create-rat n d) là mu s d≠0. Thành phn preconditions có th vng mt nu
không có điu kin đu.
Thành phn axioms mô t các chc nng vn dng hàm, phi đc khai báo rõ ràng,
đy đ và d hiu. Mi vn dng hàm có dng mt mnh đ lôgích hp thc luôn có giá tr
true, ngha là mt hng đúng (tautology). Khi cn s dng các bin, ngi ta s dng t
khoá var đ khai báo trc tên các bin, tng t trong các ngôn ng lp trình mnh lnh
nh C, Pascal...
Chú ý rng kiu tin đnh number cng là mt kiu tru tng : bi vì ta có th bit các
phép toán s hc trên các s nh +, −, *, ... nhng ta li không bit cách biu din chúng
trong b nh. Chng hn sau đây là đc t kiu s t nhiên
Ν
(natual number) :

types nat
functions
0 : -> nat ; 0 là s t nhiên đu tiên, là mt hng (constant)
succ : nat -> nat ; hàm successor ly phn t k tip
+ : nat × nat -> nat
– : nat × nat -> nat
* : nat × nat -> nat
^ : nat × nat -> nat
= : nat × nat -> boolean
66 LP TRÌNH HÀM
axioms var X, Y : nat
X + 0 == X
X + succ(Y) == succ(X + Y)
0 – X ⇔ 0 ; ch làm vic vi 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 ngha b đôi
Trong ngôn ng Scheme, mt b đôi, hay còn đc gi là cp có du chm (dotted pair),
là kiu d liu phc hp tng t bn ghi gm mt cp giá tr nào đó có th t. Có mt du
chm đ phân cách hai giá tr lu gi trong b đôi và các du cách đ phân cách gia du
chm và các giá tr. Phn t (trng) th nht đc gi là car, phn t th hai cdr.
Các tên car và cdr

1
xut hin khi John Mc. Carthy đ xut ngôn ng Lisp chy trên
IBM-704 nm 1956 và đc gi li theo truyn thng.
Gi s any là mt kiu d liu nào đó ca Scheme, ký pháp ca cu 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 kiu tru tng b đôi, đ to ra b đôi, ngi ta s dng cons :
(cons s
1
s
2
)
--> (<giá tr ca s
1
> . <giá tr ca s
2
>)
Sau đây là mt ví d v mt b đôi : :
(cons ’year 2004) ; gm mt ký hiu và mt s
−-> (year . 2004)
(cons 29 #t) ; gm mt s và mt tr lôgích
−-> (29 . #t)

nh ngha bin có giá tr là mt b đôi :


1
car = content of the address register, cdr = content of the decrement register.

KIU D LIU PHC HP 67
(define x (cons 1 2))
x
--> (1 . 2)
(car x)
--> 1
(cdr x)
--> 2
Trong máy, b đôi đc biên dch nh mt cp con tr tr đn car và cdr.

x
car cdr
1 2
Hình III.1. Biu din các b đôi nh con tr.
Các thành phn car và cdr ca b đôi có vai trò đi xng nhau : hàm car tng ng
vi thành phn đu tiên và hàm cdr tng ng vi thành phn th hai. Ta có quan h tng
quát nh sau :
(car (cons a b)) = <giá tr ca a>
(cdr (cons a b)) = <giá tr ca b>
(car x)
--> 1 x = (1 . 2)
(cdr x)
--> 2
(cdr (car y))

--> 2
V t pair? dùng đ kim tra s-biu thc có phi là mt b đôi hay không ?
(define x ’(1 . 2))
(pair? x)
--> #t ; x = (1 . 2) là mt b đôi
(pair? (cdr x))
--> #f ;(cdr x) = 2 không phi là mt b đôi
V t eq? hay đc dùng đ kim tra hai b đôi có đng nht vi nhau không. Thc cht,
eq? kim tra tính đng nht ca hai con tr.
Ng ngha ca 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 mt b đôi
(eq? x y)
--> #t
Tuy nhiên :
68 LP TRÌNH HÀM
(eq? x (cons 1 2))
--> #f
bi vì x không tr đn b đôi mi đc to ra bi cons !
III.3.3. t bin trên các b đôi
Nh cons, car, cdr, ta đã đnh ngha các b đôi nh là d liu s cp. T đó, ta có th
thay đi b đôi nh các hàm đt bin set-car! và set-cdr!. Các hàm này không to ra
b đôi mi, nhng làm thay đi b đôi đang tn ti. Scheme quy c các hàm làm thay đi
trng thái mt đi tng có tên gi kt thúc bi du chm than !. Ngi ta gi đó là nhng
hàm đt bin (mutator functions). Gi s kiu b đôi đc to ra t hai kiu d liu nào đó
ca Scheme là T1 và T2, ta vit quy c doublet(T1, T2), khi đó các hàm set-car!
và set-cdr! đc b sung vào phn đc t hàm nh sau :

functions
set-car! : doublet(T1, T2) × T1 → any
set-cdr! : doublet(T1, T2) × T2 → any
Ng ngha ca 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. Biu din các b đôi đt bin.
Trc (a) và sau khi (b) thay đi b đôi
Khi đó nu thc hin :
(set-car! x 3)
(set-cdr! x 4)
thì giá tr ca con tr x không thay đi, c hai trng hp đu cùng tr đn mt b đôi. Tuy
nhiên trong trng hp sau, ni dung ca b đôi đã thay đi, lúc này b đôi cha hai con tr
tr đn hai v trí biu din ln lt 3 và 4. Riêng hai v trí cha ln lt 1 và 2 vn nh c.
Kim tra giá tr ca con tr x :
x

--> (3 . 4)
KIU D LIU PHC HP 69
III.3.4. ng dng b đôi
1. Biu din các s hu t
Scheme s dng b đôi đ biu din kiu d liu danh sách. Trc khi trình bày các phép
x lý trên danh sách, ta có th s dng b đôi đ minh ho mt cách biu din các s hu t,
là mt cp s nguyên (trong Scheme, s hu t có dng tin đnh n/d).
Gi s ta cn xây dng mt tp hp các hàm cho phép x lý các s hu t nh : cng (+),
tr (−), ... Gi s kiu s hu 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ò to s hu t t t s n và mu s d. Các hàm numer tr
v t s (numerator) và denom tr v mu s (denominator) ca s hu t là các hàm tip
nhn. Hàm =rat là hàm kim tra hay chuyn kiu, kim tra nu hai s hu t là bng nhau.
Khi r là mt s hu t, r=n/d, thì t s ca r là n và mu s là d≠0. Hai s hu t
r1=n1/d1 và r2=n2/d2 bng nhau khi và ch khi n1.d2 =n2/d1.
Chú ý rng phn functions mi ch đnh ngha cú pháp ca hàm, cha xây dng ng

ngha cho nó. Các tên phép toán có mt trong ký pháp là cha đ, mà cn có thêm phn
axioms. Ví d các đnh ngha trong Scheme sau đây tuy đúng đn v mt cú pháp, nhng
hoàn toàn không có ngha s dng đi vi kiu tru tng 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) to s hu t t t s n và mu s d
(numer x) tr v t s (numerator) ca s hu t x
(denom x) tr v mu s (denominator) ca s hu t x
(=rat r1 r2) kim tra nu r1 và r2 đu cùng là mt s hu t
S dng b đôi, ta đnh ngha li bn hàm trên đây theo cách hot đng đc mô t trong
thành phn axioms nh sau :
(define (create-rat n d) (cons n d))
70 LP 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 dng các đnh ngha trên đ to ra các s hu 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 tip tc đnh ngha các hàm mi trên các s hu t +rat, -rat, *rat, /rat (tng
ng vi các phép toán +, −, *, / ) và hàm chuyn mt s hu t thành s thc rat->real
đ b sung vào kiu d liu tru tng 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, ngi đc có th t mình vit b sung phn ng ngha cho các hàm mi. Các
hàm này đc đnh ngha 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))))
; chuyn mt s hu t thành s thc :
(define (rat->real r) (/ (numer r) (denom r)))
KIU D LIU PHC HP 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 hu t dn đn nhiu mc lp trình có đ tru tng gim dn nh sau :
− X lý các s hu t
− X lý các phép toán trên các s hu t : +rat, -rat, *rat, /rat, =rat
− X lý s khi trên các s hu t : create-rat, numer, denom
− X lý s khi trên các b đôi : cons, car, cdr
Khi thit k chng trình đnh hng cu trúc trong phng pháp tinh ch tng bc,
ngi lp trình quan tâm đn các mc tru tng cao hn, bng cách u tiên chc nng x lý
nh th nào, mà chn chm li (trì hoãn) cách biu din các cu trúc d liu tng ng. i
vi các s hu t, ta có th biu din chúng di dng phân s rút gn nh sau :
(define (create-rat n d)
(define g (gcd (abs n) (abs d)))
; Chú ý hàm th vin gcd (greatest common divisor)
; ch tính c s chung ln nht cho các s nguyên dng
(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 ngha hàm trên đây không làm thay đi tính cht s hc ca các s hu t, cng
nh không làm thay đi mc đ tru tng ca bài toán. Hai cách đnh ngha th tc

create-rat va trình bày là đáng tin cy vì trc khi gi chúng, ta đã gi thit rng điu
kin đu (d

0) đã đc tho mãn.
D phòng và khc phc nhng sai sót có th xy ra, trong khi lp trình hay trong quá
trình khai thác ca ngi s dng, bng cách kim tra điu kin đu, đc gi là phng
pháp lp trình d phòng (defensive programming). Phng pháp lp trình này làm cho
chng trình tr nên đáng tin cy và d thuyt phc ngi s dng.
Ta vit li th tc create-rat nh sau :
(define (create-rat n d)
(if (zero? d) ; kim tra điu kin đu
(display ”ERROR: *** Mu s bng 0 !”)
(cons n d)))
(create-rat 4 0)
--> ERROR: *** Mu s bng 0 !
72 LP TRÌNH HÀM
2. Biu din hình ch nht phng
Gi s cn mô hình hóa các hình ch nht trong ta đ phng xoy có các cnh song song
vi các trc to đ, ta có th có nhiu phng pháp mô t hình ch nht qua ta đ nh sau :
1. Ta đ ca hai đnh đi din.
2. Ta đ tâm đim và hai đ dài cnh.
3. Ta đ ca đnh di cùng bên trái và hai đ dài cnh.
Ta có th chn phng pháp th 3. Gi s x, y là ta đ ca đnh A (xem hình v), y là đ
dài ca cnh song song vi 0x và H là đ dài ca cnh song song vi 0y. Ta cn đnh ngha
mt hàm đ ánh x mt hình ch nht thành mt đi tng Scheme, gi là biu din trong
(internal representation) ca hình ch nht.

Hình III.2. Biu din các thành phn ca mt hình ch nht.
n đây, ta có th ngh đn nhiu cách biu din nh sau :
A

y
0 x
L
H
• S dng b đôi ((x y) . (L H)),
• hoc s dng b đôi ((x . y) . (L . H)),
• hoc s dng mt danh sách gm các thành phn (x y L H) (cu trúc danh sách s
đc trình bày trong mc sau),
• v. v...
Tm thi ta cha chn cách biu din hình ch nht (da theo quan đim ca phng
pháp lp trình cu trúc : c gng trì hoãn vic khai báo d liu trong chng mc có th). Ta
xây dng hàm cons-rectangle đ to mi mt hình ch nht theo các thành phn x, y, y,
H. T mt cách biu din trong nào đó ca hình ch nht, ta cn xây dng các hàm tip cn
đn các thành phn này là value-x, value-y, value-L, value-H. Các hàm này thc
hin các phép toán trên hình ch nht mt cách đc lp vi biu din trong đã cho.
u tiên, ta cn xây dng v t (in? xo yo R) kim tra nu đim M có to đ x
o
, y
o

có nm bên trong (hay nm trên các cnh) ca mt hình ch nht R đã cho. Ta thy đim M
phi có ta đ ln hn đnh di cùng bên trái và thp hn đnh cui cùng bên phi ca R. Ta
có chng 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)))))
KIU D LIU PHC HP 73
Bây gi xây dng v t (inclus? R1 r2) đ kim tra hình ch nht R1 có nm bên
trong hình ch nht R2 không ? Nu đúng, tr v #t. Mun vy, ch cn R2 cha hai đnh đi
din ca R1. Ta có chng 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))))
 tnh tin (translate) mt hình ch nht R theo mt véct V có các thành phn a, b, ch
cn tnh tin đnh di cùng bên trái ca nó. Ta có hàm tnh tin 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. Tnh tin mt hình ch nht.
Nh đã trình bày, vic x lý trên các hình ch nht không ph thuc vào cách biu din
d liu. Khi cn thay đi cách biu din, ch cn vit li các hàm to mi hình ch nht và
các hàm tip cn đn các thành phn, mà không cn thay đi các hàm in?, inclus?, và
translate.
Chng hn ta có th biu din hình ch nht bi b đôi :
((x . y) . (L . H))
Khi đó, cách biu din trong ca hình ch nht đc xây dng 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) ; tng đng vi '((1 . 2) . (3 . 4))
b
V
y
0 x a
L
H
74 LP TRÌNH HÀM
(value-L R)
--> 3
(value-H R)
--> 4
III.4 Kiu d liu danh sách
III.4.1. Khái nim danh sách
Khi gii mt s bài toán, ngi ta thng phi nhóm nhiu d liu thành mt d liu t
hp duy nht.  nhóm các ngày trong tun là Monday, Tuesday, Wednesday, Thursday,
Friday, Saturday và Sunday thành mt t hp d liu, ngi ta s dng cu trúc danh sách có
dng mt s-biu thc nh sau :
(mon tue wed thu fri sat sun)
Tuy nhiên, nu đnh ngha :
(define week-list)
(mon tue wed thu fri sat sun))

--> *** ERROR-unbound variable: mon
thì ta gp li s sai sót v các ký t. Scheme xem danh sách này nh là mt li gi hàm có
tên mon vi các tham đi là tue, ... sun và, nh đã thy, ta cha h đnh ngha hàm mon
(!).  đnh ngha danh sách trên, Scheme s dng phép toán trích dn :
(define week-list
’(mon tue wed thu fri sat sun))
Khi đó ta có week-list là bin có giá tr là mt danh sách các ký t :
week-list
--> (mon tue wed thu fri sat sun))
 x lý thun tin, ngi ta s dng bin L đ ch đnh mt danh sách, ta có :
(define L ’(1 2 3 4 5))
L
--> ’(1 2 3 4 5)
Có th s dng các danh sách phc tp hn đ biu din các biu thc. Có th xây dng
các biu thc s hc, biu thc cha các d liu ký hiu, hay cha các danh sách lng nhau.
Mt biu thc khi đc trích dn đc gi là mt biu thc trc kin (literal expression) vì
rng giá tr ca nó chính là vn bn (text) to ra nó. Biu thc trc kin có vai trò là mt
hng :
Cho trc mt danh sách, khi ta nói đ dài hay s phn t ca danh sách (length) là s
phn t ca danh sách đó. Sau đây là mt s ví d :
’(1 2 2 3) ; là mt danh sách gm 4 s nguyên
--> ’(1 2 2 3)
’(one + two = three) ; là mt danh sách gm 5 ký hiu tu ý
--> (one + two = three)
KIU D LIU PHC HP 75
’() ; danh sách rng (empty list) không có phn t nào
--> ’()
’(* 4 5) ; không phi là mt biu thc tính đc
--> (* 4 5)
Nu ta cn biu din các thông tin v mt trang lch công tác trong ngày, ta có th s

dng mt danh sách dng :
(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))))
Kiu d liu danh sách cn có th thut đ có th đc vào và in ra. Scheme cho phép xây
dng các danh sách không thun nht (heterogeneous list) v kiu. Chng hn :
(define L ’(1 (2 3) x "bonjour"))
(list-ref L 0)
--> 1
(list-ref L 3)
--> "bonjour"
Tr trng hp ngc li,  đây ta ch nên x lý các danh sách thun nht (homogeneous
list) là danh sách ch cha cùng mt kiu d liu T đã cho : cha toàn các s, hoc ch cha
toàn các ký hiu, v.v... Th t ca các phn t xut hin trong mt danh sách là có ý ngha.
Mt trong nhng đc trng ca các chng trình h Lisp là có cùng bn cht vi d liu.
Chng hn, đnh ngha hàm (define...) cng là mt danh sách. Ngi ta gi mt danh
sách con (sub-list) là mt phn t ca danh sách đã cho. Phn t này có th là mt danh
sách, hay là mt danh sách con ca mt 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). Mt danh sách không có danh sách
con đc gi là mt danh sách phng (plate list).
Ngi ta nói rng mt phn t là  mc mt (first level) ca mt danh sách nu phn t

này không  trong mt danh sách con.
Các phn t  mc mt ca danh sách va nêu trên đây là :
a, (b c), (), ((c)), c
Ngi ta gi các phn t  mc th n là nhng phn t  mc mt ca mt danh sách con
đc đt  mc n-1.
 sâu (depth) ca mt danh sách là mc ca mt phn t  mc cao nht. Chng hn
danh sách trên đây có đ sâu là 3.
 dài (length) ca mt danh sách là s các phn t  mc th nht, đc tính nh hàm
tin đnh length ca Scheme :
76 LP 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 phn t mc 1
b c ( c ) các phn t mc 2
c các phn t mc 3
Hình III.4. Biu din các mc ca mt danh sách phc hp.
nh ngha kiu d liu tru tng danh sách
Có th có nhiu cách khác nhau đ đnh ngha mt danh sách. Sau đây là đc t kiu d
liu tru tng danh sách list(T) s dng các phép toán c bn cons, car và cdr, trong đó,
T là kiu phn t ca 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 vi x ≠ 0
cdr(x) ch xác đnh vi 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 dng danh sách
III.4.2.1. Các phép toán c bn cons, list, car và cdr
Scheme s dng b xây cons đ to mt danh sách :
(cons s L)
tr v mt danh mi bng cách thêm giá tr ca s trc danh sách L.
(cons ’a ’())
--> (a)
(cons (+ 5 8) ’(a b))
--> (13 a b)
Scheme có th nhn bit mt danh sách và đa ra theo cách riêng :
KIU D LIU PHC HP 77
(cons 1 2)
--> (1 . 2)
(cons 1 ’()) ; chú ý du quote do danh sách rng làm tham đi
--> (1) ; không phi (1 . ())
(cons 1 (cons 2 (cons 3 ())))
-> (1 2 3) ; không phi (1 . (2 . (3 . ())))
 xây dng danh sách, Scheme thng s dng hàm list. Quan h gia b xây cons
và cu trúc list đc tóm tt bi đng thc 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 nhn mt s tham đi tùy ý là các s-biu thc đ tr v mt danh sách mi t
hp t các giá tr ca 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 gm 3 phn t, phn t th nht là du + :
’(+ 1 2)
--> (+ 1 2)
Chú ý kiu d liu ca các phn t danh sách :
(list a b c)
--> Error: undefined variable a
(list ’a ’b ’c)
’ (a b c)
Các dng car và cdr dùng đ tip cn đn các phn t ca mt danh sách khác rng.
Hàm (car L) tr v phn t đu tiên ca 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 đã ly đi phn t đu tiên
(cdr ’(a b c))
--> (b c)
Ta cng có các đng thc sau :
(car (cons s L)) =
-
s
(cdr (cons s L)) = L
-

78 LP TRÌNH HÀM
Vi quy c du gch trên tên ch đnh giá tr ca mt biu thc. Trong Scheme, car hay cdr
ca mt danh sách rng đu dn đn mt li sai, trong khi trong các ngôn ng phát trin t Lisp thì
thng có kt qu tr v là mt danh sách rng. Nu mun thay đi tên các hàm car và cdr
này, ch cn đnh ngha các hàm đng ngha head và tail nh sau :
(define head car)
(define tail cdr)
Tuy nhiên, không nên đnh ngha nh vy, vì rng s gp khó khn khi công vic lp trình
có s tham gia ca nhiu ngi. Bng cách t hp các hàm car hoc cdr, ta có th tip cn
đn bt k mt phn t nào ca mt danh sách :
(car (cdr week-list))
--> mardi

(car (cdr (cdr week-list))))
--> mercredi
Cách vit t hp trên đây s làm ngi đc khó theo dõi. Chính vì vy ngi ta đa ra
mt h thng vit tt đ ch đnh s t hp ti đa là bn car hay cdr. Scheme quy c :
cx
1
x
2
x
3
x
4
r vi x
j
là mt a hoc mt 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ó tt c 28 hàm ghép nh vy. 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 dng hai s đ tng quát đ đnh ngha các hàm x lý danh sách :
S đ đ quy tuyn tính :
(define (<fname> <arg>)
(if (null? <arg>)
<init-val>
(<func> (car <arg>) (<fname> (cdr <arg>)))))
S đ lp :
(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? ca Scheme cho phép kim tra mt danh sách rng.
(null? '())
--> #t
KIU D LIU PHC HP 79
III.4.2.2. Các hàm x lý danh sách
Th vin ca Scheme có sn nhiu hàm x lý danh sách. Sau đây ta đc t b sung mt s
hàm x lý danh sách thông dng vào kiu d liu tru tng 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 ca danh sách đc đnh ngha nh sau :
(define (length L)
(if (null? L)
0
(+ 1 (length (cdr L)))))
Hàm length có chi phí tuyn tính. Ví d :
(length (list 1 2 3))
--> 3
(= (length (list 1 2 3 4)) 0)
--> #f
Các danh sách rng có th đc to ra bi hàm (list) không có tham đi, hoc s
dng trc kin ’(). Ví d :
(= (length (list)) 0)
--> #t
(= (length ’()) 0)

--> #t
(null? (list 1 2 3 4))
--> #f
(null? (list))
--> #t
(null? ’())
--> #t
Scheme không tha nhn dng ’() là mt biu thc, mà xem ’() là mt giá tr. Vì
vy, đ s dng danh sách rng, ta cn trích dn :
’()
--> ‘()
Hàm append nhn mt s tham đi tùy ý, mi tham đi có giá tr là mt danh sách đ
tr v mt danh sách mi, là ghép (concatenation) tt c các danh sách tham đi này. Hàm
append đc đnh ngha nh sau :
(define (append L1 L2)
(if (null ? L1)
L2
(cons (car L1) (append (cdr L1) L2))))
Hàm append có chi phí tuyn tính theo L1. Ví d :
80 LP 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 nhm ln cách s dng ca 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 nghch đo (reverse list) ca tham đi, đc đnh ngha
đ quy nh sau :
(define (reverse L)
(if (null? L)
’() ; hoc (list)
(append (reverse (cdr L)) (list (car L))))
Chi phí ca hàm reverse là tuyn 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ú ý ngi lp trình thng gp li sai v kiu gia car, cdr và list. Nên thng
xuyên thc hin vic phân tích kiu. Chng hn nu dòng cui trong đnh ngha hàm
reverse vit li là :
(append (reverse (cdr L)) (car L)))) ; quên đt tin t list
thì s gây ra li sai vì (car L) là mt phn t, không phi là mt danh sách. Ta có th
đnh ngha li hàm reverse nh phép lp 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 ’())) ; hoc (list)
(reverse (list 1 2 3 4))
--> (4 3 2 1)
Chi phí ca hàm reverse vn tu thuc tuyn tính vào L.
2. Các hàm tham chiu danh sách

(list-ref L n)
(list-tail L n)
(list? L)
KIU D LIU PHC HP 81
Hàm (list-ref L n) tr v phn t th n ca danh sách L, vi quy c rng trong
mt danh sách, các phn 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 phn t còn li ca danh sách L sau khi b đi n phn 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) kim tra L có phi là mt 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 chuyn đi kiu
(string->list str)
(list->string L)
(string->number str [radix])
82 LP TRÌNH HÀM
Hàm (string->list str) chuyn mt chui thành mt danh sách. Hàm này có th
đc đnh ngha 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) chuyn mt ch thng thành ch hoa,
còn hàm (char-downcase ch) chuyn mt ch hoa thành ch thng.
Hàm (list->string L) chuyn mt danh sách thành mt chui. Hàm này có th
đc đnh ngha 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]) chuyn mt chui str thành mt s h
10 (mc nhiên). Nu có thêm thành phn radix là mt s nguyên ch c s h đm 2, 8, 10
hay 16 thì s trong h đm tng ng s đc đi sang h 10 :
(string->number ”9999”)
--> 100
(string->number ”1e3”)
--> 1000.0
(string->number "1ABC" 16)
--> 6844
KIU D LIU PHC HP 83

4. Các hàm so sánh danh sách
 so sánh các danh sách, ngi ta dùng v t equal? :
(equal? ’(a b 3) (list ’a ’b (+ 1 2)))
--> #t
(equal? ’(()) ’())
--> #t
Ta đã gp có các v t xác đnh kiu d liu danh sách ca mt s-biu thc :
(list? s)--> #t nu giá tr ca biu thc s là mt danh sách bt k
(null? s)--> #t nu giá tr ca biu thc s là mt danh sách rng
(pair? s)--> #t nu giá tr ca biu thc s là mt danh sách khác rng.
ôi khi ngi ta cn kim tra nu mt s-biu thc không phi là mt danh sách và kim
tra nu mt danh sách có phi đc rút gn thành mt phn t duy nht không. Ta thêm các
v t sau đây vào th vin các tin ích vn cha có trong th vin tin đnh ca Scheme :
V t atom? kim tra nu mt s-biu thc không phi là mt danh sách :
(define (atom? s)
(not (pair? s)))
V t singleton? kim tra mt danh sách đc rút gn thành mt phn t duy nht :
(define (singleton L)
(null? (cdr L)))
Ngi ta cng thng phi kim tra nu mt đi tng là mt phn t  mc th nht ca
mt danh sách. Mun vy, ngi ta s dng mt hàm tin đnh (member S L). Hàm này
tr li mt phn ca danh sách L cha phn đu tiên (first ocurrence) ca giá tr s, hoc tr v
#f nu danh sách L không có. S so sánh đc thc hin nh kim tra equal? :
(member ’(b) ’(a b c d))
--> #f
(member ’(b) ’(a b c (b) d))
--> ((b) d)
Scheme xem các giá tr bng nhau  các mc đ chính xác khác nhau. V t equal? so
sánh các cu trúc nhng không phi là s so sánh tên ca các đi tng. Tên ca các đi
tng đc kim tra nh v t eq? và tn ti mt v t so sánh ít cht ch hn là eqv?.

Nh vy, đ so sánh các danh sách hoc các chui, ta s dung equal?, đ so sánh các
s, s dng = và đ so sánh các ký hiu, s dng eq?. Scheme có ba hàm so sánh hot đng
nh sau :
member s dng phép kim tra equal?
memv s dng phép kim tra eqv?
memq s dng phép kim tra eq?
Sau đây là đnh ngha ca hàm member :
(define (memq s L)
(cond
((null? L) #f)
((eq? (car L) s) L)
(else (memq s (cdr L)))))
Ví d :

×