N u c n liên k t các bi n theo dãy, Scheme có d ng đ c bi t let* :
(let* ((v1 s1)... (vk sk)) body)
S liên k t c a let* th hi n ch giá tr c a vi dùng đ tính sj, n u i<j (viđ ng tr c sj).
(let* ((a (* a a)) ; a = 4
(b (* 3 a)) ; b = 12
(c (* a b))) ; c = 48
(+ a b c )) --> 64
Th t v y, trong thân c a let*, bi n a=4 do tr c đó, a=2, bi n b=12 do a=4, bi n c=48 do a=4 và b=12.
D ng let* th c ra không th t c n thi t vì có th đ c bi u di n b i các let l ng nhau. Ví d trên đây đ c vi t l i theo d ng let nh sau :
(let ((a (* a a))) (let ((b (* 3 a)))
(let ((c (* a b))) (+ a b c)))) --> 64
D ng let l ng nhau đ c dùng đ làm rõ s ph thu c gi a các bi u th c. M t cách t ng quát, quan h gi a let và let* nh sau :
(let* ((v1 s1) ... (vk sk)) body) ⇔ (let ((v s1 1)) (let ((v2 s2)) ... (let ((vk sk)) body) ... )) II.3.3.3. nh ngh a các v t
Nh đã nói, tên các v t trong Scheme đ u đ t m t d u ch m (?) h i sau cùng. NSD có th đnh ngh a các v t và c ng ph i tuân theo quy c này. Ch ng h n ta đnh ngh a l i v t ki m tra m t s nguyên là ch n (even) hay l (odd) mà không s d ng các v t even? và odd? có s n trong th vi n c a Scheme :
(define (is-even? n) (if (= n 0) #t (is-odd? (- n 1)))) (define (is-odd? n) (if (= n 0) #f (is-even? (- n 1)))) (is-even? 4) --> #t (is-odd? 5) --> #t
V t sau đây ki m tra m t s nguyên n trong ph m vi 2..100 có là s nguyên t hay không ? Ta l p lu n nh sau : n không ph i là s nguyên t n u n chia h t (th ng s l n h n 1) cho các s 2, 3, 5, 7, ... sqrt(n). V y n u 10<n≤100 thì sqrt(100) = 10 và do v y, n không th là b i c a các s 2, 3, 5, 7.
(define (multiple2357? n)
(or (zero? (remainder n 2)) (zero? (remainder n 3)) (zero? (remainder n 5)) (zero? (remainder n 7))))
Các s 2, 3, 5, 7 và các s nguyên trong ph m vi 100 không ph i là b i c a các s này đ u là s nguyên t : (define (prime<=100? n) (if (or (= n 2) (= n 3) (= n 5) (= n 7) (not (multiple? n))) #t #f))
Sau đây ta vi t v t ki m tram t n m d ng l ch đã cho có ph i là n m nhu n hay không ? M t n m là nhu n n u chia h t cho 400 ho c n u không ph i thì n m đó ph i
chia h t cho 4 và không chia h t cho 100. Tr c tiên ta vi t v t ki m tra m t s này có
chia h t m t s khác hay không.
(define (divisibleBy? number divisor) (= (remainder number divisor) 0)) (define (isBissextile? year)
(or (divisibleBy? year 400)
(and (divisibleBy? year 4)
(not (divisibleBy? year 100))))) (isBissextile? 1900) --> #f (isBissextile? 2004) --> #t II.3.4. S đ đ quy và s đ l p II.3.4.1. S đ đ quy
Có nhi u s đ đ quy (recursive schema) đ c ng d ng quen thu c trong l p trình, ngôn ng Scheme s d ng s đ đ quynguyên thu (primitive) có cú pháp nh sau :
(define (<name> <arg>) (if (zero? <arg>)
<init-val>
(<func> <arg> (<name> (- <arg> 1)))))
đây, c <init-val> và <func>đ u ch a đ c đnh ngh a theo hàm <name>. S đ đ quy đ c gi i thích m t cách t ng quát nh sau :
Có m t ho c nhi u c a ra t ng ng v i đi u ki n đã đ c tho mãn đ d ng quá trình đ quy : n u <arg> = 0 thì c a ra l y giá tr tr v <init-val>.
•
• Có m t ho c nhi u l i g i đ quy, ngh a là g i l i chính hàm đó, sao cho trong m i tr ng h p, giá tr c a tham đ i <arg> ph i h i t v m t trong các c a ra đ d ng, thông th ng gi m d n, ch ng h n tham đ i m i là <arg>-1.
Ph ng pháp l p trình đ quy mang tính t ng quát và tính hi u qu khi gi i quy t nh ng bài toán tính hàm có đ l n d li u phát tri n nhanh. áp d ng k thu t đ quy, luôn luôn ph i tìm câu tr l i cho hai câu h i sau đây :
1. Có t n t i hay không các tr ng h p đ c bi t c a bài toán đã cho đ nh n đ c m t l i gi i tr c ti p d n đ n k t qu .
2. Có th nh n đ c l i gi i c a bài toán đã cho t l i gi i c a c ng bài toán này nh ng đ i v i các đ i t ng nh h n theo m t ngh a nào đó ?
N u có câu tr l i thì có th dùng đ c đ quy. Sau đây là m t s ví d .
II.3.4.2. Ví d
1. Tính t ng bình ph ng các s t 1 đ n n
Xét hàm SumSquare tính t ng bình ph ng các s t 1 đ n n :
SumSquare = 1 + 22 + 32 + ... + n2
B ng cách nhóm n−1 s bình ph ng phía bên ph i, t c t 1 đ n (n−1)2, ta nh n đ c quan h truy h i nh sau :
SumSquare(n) = SumSquare (n−1) + n2 ,
Quy c SumSquare (0) = 0, ta đnh ngh a hàm SumSquare trong Scheme nh sau : (define (SumeSquare n) (if (zero? n) 0 (+ (SumeSquare (- n 1)) (* n n)))) (SumeSquare 3) --> 14 nh ngh a hàm nh trên là đ quy vì hàm c n đnh ngh a l i g i l i chính nó. ó là l i g i đ quy (SumSquare (- n 1)). 2. Tính giai th a tính n ! = 1* 2 * ...* n v i ta có : 0 ! = 1 n ! = n *(n−1) ! v i n > 1 T đó : (define (fac n) (if (zero? n) ; ho c (<= n 0) đ đ m b o x lý m i s n 1 (* n (fac (- n 1))))) (fac 5) --> 120 3. Hàm Fibonacci
Có th cùng lúc có nhi u l i g i đ quy trong thân hàm. M t hàm c đi n khác th ng đ c minh h a cho tr ng h p này là hàm Fibonacci. Hàm fib đ c đnh ngh a t quan h truy h i :
f (0) = 0 và f (1) = 1. V i n≥ 0, thì f (n + 2) = f (n + 1)+ f (n).
( - ) = n n φ ψ f(n) 5 v i (1 + ) = 5 2 φ ~ 1.6, và = (1 - 5) 2 ψ ~ 0.6
đây, fđ c g i là s vàng (golden number), v i ψ là nghi m c a ph ng trình :
x2 = 1 + x.
Hàm fib trong Scheme đ c đnh ngh a nh sau : (define (fib n)
(if (<= n 1) n
(+ (fib (- n 1)) (fib (- n 2))))) Ta nh n th y r ng có nhi u s Fibonacci đ c tính nhi u l n : (fib 4)
; = (+ (fib 3) (fib 2))
; = (+ (+ (fib 2) (fib 1)) (+ (fib 1) (fib 0)))
; = (+(+(+(fib 1)(fib 0))(fib 1))(+(fib 1)(fib 0))) ; = (+ (+ (+ 1 0) 1) (+ 1 0))
; = (+ (+ 1 1) 1) ; = (+ 2 1)
--> 3
4. Tính các h s nh th c
Tính các h s nh th c hay t h p : s phép ch n k trong b n phân t . G i (n k) là h s nh th c c n tìm, v i 0 ≤k≤n, n ≥ 0, ta có : ! ( ) ( - )! * ! n n k n k k = v i : (n 0) = (n n) = 1 (n + 1 k) = (n k) + (n k - 1)
Ch ng h n (5 2) = 10. Ta xây d ng hàm coef-binomial nh sau : (define (coef-binomial n k) (cond ((zero? k) 1) ((= k n) 1) (else (+ (coef-binomial (- n 1) k) (coef-binomial (- n 1) (- k 1)))))) (coef-binomial 5 2) --> 10 (coef-binomial 0 0) --> 1 II.3.4.3. Tính d ng c a l i g i đ quy
M t l i g i đ quy ph i luôn luôn ch a ít nh t m t c a ra, còn đ c g i là tr ng h p c s , đ tr v k t qu c a hàm mà đó không ph i là m t l i g i đ quy. Vì n u không, s x y ra hi n t ng g i nhau (l p đi l p l i) vô h n l n. Nh v y cách đnh ngh a các hàm trong các ví d trên đây là h p lý vì đ u có c a ra.
Ch ng h n c a ra c a âënh nghéa hàm fac trên đây là (fac 0) ng v i 0!=1. Tuy nhiên, m t th t c đ quy có c a ra (c a ra không g i đ quy) ch a đ đi u ki n đ k t thúc.
minh ho ta hãy xét m t cách khác đnh ngh a hàm fac là hàm bad-fact nh sau : (define (bad-fact n) (if (= n 5) 120 (quotient (bad-fact (+ n 1)) (+ n 1)))) V i l i g i n=3, (n<5), ta có : (bad-fact 3) ; = (quotient (bad-fact 4) 4)
; = (quotient (quotient (bad-fact 5) 5) 4)
; = (quotient (quotient 120 5) 4) ; = (quotient 24 4) --> 6 K t qu đúng, nh ng v i l i g i n=6, (ho c v i m i n>5), ta có : (bad-fact 6) ; = (quotient (bad-fact 7) 7)
; = (quotient (quotient (bad-fact 8) 8) 7)
; ...
Do vi ph m đi u ki n «hàm đ quy g i l i chính nó nh ng có giá tr tham đ i nh h n», rõ ràng vi c tính giai th a c a hàm bad-fact s không bao gi d ng l i.
Ch ng h n, l i g i (fac −2) s kéo theo l i g i (fac −2), kéo theo l i g i (fac −3), v.v... C hai tr ng h p đ u gây ra thông báo l i (ho c treo máy) :
*** ERROR −−− Stack overflow
Chú ý đ i v i hàm fac, n u l y n <0, thì c ng gây ra m t vòng l p vô h n. T nh ng quan sát trên đây, ng i ta đ t ra ba câu h i sau :
1. Có th suy lu n ngay trên các dòng l nh c a ch ng trình đ ch ng minh m t đnh ngh a hàm là đúng đ n ? Câu tr l i là có th .
2. Có ph i là ng u nhiên mà hàm fac đ c đnh ngh a đúng còn hàm bad-fact thì không đúng ? Câu tr l i là không ph i.
3. Có t n t i hay không nh ng s đ l p trình áp d ng đ c cho các đnh ngh a hàm đ
ch y đúng đ n ? Câu tr l i là có.
T t c nh ng v n đ trên đ u d a trên khái ni m quy n p (induction) mà phép l p là m t tr ng h p đ c bi t5. ch ng minh hàm P đnh ngh a đúng (tính đúng) m t hàm f nào đó, c n ph i ch ng minh P tho mãn hai đi u ki n là :
− Pđúng đ n t ng ph n (partial correction). − P d ng.
Trong tr ng h p tính giai th a, ta bi t r ng 0! = 1, và n! = n*(n−1)!. Bây gi ta c n ch ng minh tính đúng đ n t ng ph n c a fac :
• •
5
ch ng t r ng m t tính ch t P nào đó là đúng đ i v i m i s nguyên n, ng i ta l p lu n nh sau : C s quy n p : P(0) là đúng.
(fact 0) = 1 = 0!
• •
• •
N u n >0, ho c n1 = n−1, thì do gi thi t quy n p (fact n1) = n1!, do đó (fact n) = (* n (fact n1)) = n * n1! = n!
II.3.4.4. Ch ng minh tính d ng
M i l i g i đ quy, các tham đ i đ c dùng t i ph i «nh h n» so v i tham đ i ban đ u. Ph ng pháp thông d ng nh t đ đ m b o m t hàm đ quy d ng là tìm đ c m t s nguyên d ng gi m ng t sau m i l n g i đ quy. Trong tr ng h p hàm SumSquare, ta ch c n ch n tham đ i n.
Hàm fib d ng vì m i l i g i kéo theo hai l i g i đ quy, m i m t trong chúng có tham đ i m i lúc l i nh h n. V i n≥ 2, các s nguyên n−1 và n−2 gi m ng t so v i n và đ u ≥ 0.
Trong tr ng h p hàm fac, dãy các giá tr c a n là gi m ng t, và (fac n) ch g i (fact n1) n u n≠ 0, nh v y n u n≥ 0, thì n1= n−1 ≥ 0.
T đó n u giá tr ban đ u c a n là ≥ 0, thì dãy này là h u h n. M t khác, fac ch g i đ n nh ng hàm s c p (primitive) là nh ng hàm d ng.
T đó suy ra r ng (fact n) = n! v i n≥ 0
Ch ng minh tính đúng đ n t ng ph n c a hàm bad-fact :
(bad-fact 0) = (quotient (bad-fact 1) 1) = ... = 1 N u (bad-fact n1) = n1! là đúng v i n1 = n+ 1, thì :
(bad-fact n) = (quotient (bad-fact n1) n1) = n1! / n1 = n!
Ta suy ra r ng n u bad-fact d ng, thì bad-fact tính đúng giai th a cho các s n
tho mãn 0 ≤n≤ 5. Tuy nhiên bad-fact không d ng v i m i n> 6.
Th c t , ng i l p trình không s d ng cách l p lu n trên đây vì tính ph c t p và dài dòng (dài h n hàm c n ch ng minh). Tuy nhiên ng i l p trình v n c n ph i bi t đ thuy t ph c ng i khác vì không th ch ng minh tính đúng đ n m t ch ng trình qua m t vài ví d minh ho .
II.3.4.5. S đ l p
Ta l y l i phép tính giai th a : đ tính fac 3, tr c tiên b di n d ch Scheme ph i tính (fact 2), mu n v y c n ghi nh đ l n sau th c hi n phép nhân. T ng t , c n ph i tính (fact 1), tr c khi tính (* 2 (fact 1)). Nh v y đã có m t chu i các phép tính khác nhau, t ng tuy n tính v i n. Ng i ta g i đây là quá trình đ quy tuy n tính (linear recursive processus).
Trong tr ng h p tính hàm fib, lúc đ u Scheme hoãn th c hi n m t phép c ng đ ti n hành hai l i g i đ quy mà không th tính toán đ ng th i. Ng i ta g i đây là quá trình đ
quy d ng cây (tree recursive processus). Rõ ràng v i ki u quá trình này, hàm fib đòi h i m t chi phí tính toán đáng k . Bây gi ta xét m t cách tính giai th a khác :
(define (fact n) (define (i-fact p r) (if (= p 0) r (i-fact (− p 1) (* p r)))) (i-fact n 1))
Áp d ng : (fact 3) ; = (i-fact 3 1) ; = (i-fact 2 3) ; = (i-fact 1 6) ; = (i-fact 0 6) --> 6
T i m i giai đo n, không có phép tính nào b hoãn và do đó không c n ghi nh . Ta có th làm d ng quá trình tính b ng cách ch c n ghi nh m t s giá tr c đnh (là p và r), r i dùng l i sau đó.
Ng i ta g i quá trình tính trên đây là quá trình l p (iterative processus). Ng i ta c ng nói r ng hàm là đ quy k t thúc.
S d ng quá trình l p r t có hi u qu v b nh , vì v y mà trong h u h t các ngôn ng l p trình c đi n, các ngôn ng m nh l nh, ng i ta đnh ngh a các c u trúc cú pháp cho phép th c hi n các quá trình l p (các vòng l p while, for...).
B di n d ch Scheme có kh n ng x lý m t quá trình l p trong m t không gian nh không đ i nên ng i l p trình nên t n d ng kh n ng này.
M t quá trình l p c ng đ c đ c tr ng b i m t b t bi n (invariant) : đó là m t quan h gi a các tham đ i. Quan h luôn đ c gi không thay đ i khi ti n hành các l i g i liên ti p. Quan h này cho phép ch ng minh tính đúng đ n c a ch ng trình.
Ch ng h n, ta tính b t bi n c a hàm i-fact : p = 0 và p! * r = constant = p 0! * r 0 đây, p 0 và r 0 là các giá tr trong l i g i chính. Gi s r ng l i g i đ quy b o toàn quan h này :
p
0! * r
0 = (p−1)! * (p! *r) = p! * r
H n n a, tính ch t này còn đúng trong l i g i chính : hàm i-fact d ng v i giá tr p = 0
và do v y ta có r = p
0! * r
0, chính là k t qu tr v c a i-fact.
L i g i chính là (i-fact n 1), n=0, k t qu c a (fact n) s là n! * 1 = n!.
Ng i ta luôn luôn có th chuy n m t hàm đ quy tuy n tính thành đ quy k t thúc, b ng cách đnh ngh a m t hàm ph tr có m t tham đ i b sung dùng đ tích lu các k t qu trung gian (bi n r trong hàm i-fact).
S đ l p t ng quát đ đnh ngh a m t hàm fname nh sau :
(define (<fname> <arg>)
(define (<i-name <arg> <result>) (if (zero? <arg>)
<result>
(<i-name> (- <arg> 1) (<func> <arg> <result>))))
(<i-name> <arg> <init-val>))
Ng i ta c ng có th chuy n m t hàm đ quy d ng cây thành đ quy k t thúc, lúc này th i gian tính hàm đ c rút g n m t cách ngo n m c ! Ch ng h n ta vi t l i hàm tính các s Fibonacci theo ki u đ quy k t thúc :
(define (fib n)
(define (i-fib x y p) ; x=fib(n-p+1), y=fib(n-p) (if (= p 0) y (i-fib (+ x y) x (- p 1)))) (i-fib 1 0 n)) (fib 100) --> 354224848179261915075 (fib 200) --> 280571172992510140037611932413038677189525 Chú ý r ng hàm tính Fibonacci bây gi có chi phí tuy n tính !!!
II.3.5. Vào/ra d li u
Cho đ n lúc này, ta m i t o ra các hàm mà ch a nêu lên cách vào/ ra d li u. Th c t ta đã l i d ng vòng l p t ng tác nh sau :
... →đ c d li u → tính hàm → in ra k t qu → ...
M t trong nh ng nguyên lý c a l p trình Scheme là tránh tr n l n các vi c in ra k t qu v i vi c tính toán trong cùng m t đnh ngh a hàm. Tuy nhiên, trong m t s tr ng h p, v n có th tr n l n các quá trình vào/ra vào bên trong c a m t hàm.
1. c vào d li u : read
Hàm (read)đ c m t d li u b t k c a Scheme t bàn phím (là dòng vào hi n hành) và tr v giá tr đ c đ c mà không x y ra m t tính toán nào.
(read)
Sau khi th c hi n l i g i này, Scheme b t đ u tr ng thái ch ng i s d ng gõ vào t bàn phím. Gi s ng i s d ng gõ vào m t bi u th c có ch a nhi u d u cách th a (gi a + và 2, gi a 2 và 3) :
(+ 2 3)
L p t c, Scheme s tr v k t qu là m t bi u th c đúng (không còn d u cách th a) mà không tính bi u th c này.
--> (+ 2 3)
D ng t ng quát c a hàm đ c d li u c a Scheme có ch a m t tham bi n tùy ch n là m t
dòng vào (flow) th ng đ c dùng khi đ c các t p d li u (read [flow]). Khái ni m v các dòng vào/ra s xét ch ng sau.
2. In ra d li u : write và display
in ra giá tr c a m t s-bi u th c b t k ra màn hình (là dòng ra hi n hành), Scheme có