i
CHNG I. NGUYÊN LÝ LP TRÌNH HÀM
« The primary purpose of a programming language
is to help the programmer in the practice of his art »
Charle A. Hoare (Hints on programming language design, 1973)
I.1 M đu v ngôn ng lp trình
I.1.1. Vài nét v lch s
Bui ban đu
hng ngôn ng lp trình (programming language) đu tiên trên máy tính đin t là
ngôn ng máy (machine language), t hp ca các con s h hai, hay h nh phân, hay
các bit (vit tt ca binary digit) 0 và 1. Ngôn ng máy ph thuc hoàn toàn vào kin
trúc phn cng ca máy tính và nhng quy c kht khe ca nhà ch to. gii các bài
toán, ngi lp trình phi s dng mt tp hp các lnh điu khin rt s cp mà mi lnh là
mt t hp các s h hai nên gp rt nhiu khó khn, mt nhc, rt d mc phi sai sót, nhng
li rt khó sa li.
N
T nhng nm 1950, đ gim nh vic lp trình, ngi ta đa vào k thut chng trình
con (sub-program hay sub-routine) và xây dng các th vin chng trình (library) đ khi
cn thì gi đn hoc dùng li nhng đon chng trình đã vit.
Ngôn ng máy tin gn đn ngôn ng t nhiên
Cng t nhng nm 1950, ngôn ng hp dch, hay hp ng (assembly) hay cng còn
đc gi là ngôn ng biu tng (symbolic) ra đi. Trong hp ng, các mã lnh và đa ch
các toán hng đc thay th bi các t ting Anh gi nh (mnemonic) nh ADD, SUB, MUL,
DIV, JUMP... tng ng vi các phép toán s hc + - × /, phép chuyn điu khin, v.v...
Do máy tính ch hiu ngôn ng máy, các chng trình vit bng hp ng không th chy
ngay đc mà phi qua giai đon hp dch (assembler) thành ngôn ng máy. Tuy nhiên, các
hp ng vn còn ph thuc vào phn cng và xa l vi ngôn ng t nhiên (natural language),
ngi lp trình vn còn gp nhiu khó khn khi gii các bài toán trên máy tính.
Nm 1957, hãng IBM đa ra ngôn ng FORTRAN (FORmula TRANslator). ây là ngôn
ng lp trình đu tiên gn gi ngôn ng t nhiên vi cách din đt toán hc. FORTRAN cho
phép gii quyt nhiu loi bài toán khoa hc, k thut và sau đó đc nhanh chóng ng dng
rt rng rãi cho đn ngày nay vi kho tàng th vin thut toán rt đ s và tin dng. Tip
theo là s ra đi ca các ngôn ng ALGOL 60 (ALGOrithmic Language) nm 1960, COBOL
(Comon Business Oriented Language) nm 1964, Simula nm 1964, v.v...
1
2 LP TRÌNH HÀM
Phát trin ca ngôn ng lp trình
Theo s phát trin ca các th h máy tính, các ngôn ng lp trình cng không ngng
đc ci tin và hoàn thin đ càng ngày càng đáp ng nhu cu ca ngi s dng và gim
nh công vic lp trình. Rt nhiu ngôn ng lp trình đã ra đi trên nn tng lý thuyt tính
toán (theory of computation) và hình thành hai loi ngôn ng : ngôn ng bc thp và ngôn
ng bc cao.
Các ngôn ng bc thp (low-level language), hp ng và ngôn ng máy, thng ch dùng
đ vit các chng trình điu khin và kim tra thit b, chng trình sa li (debugger) hay
công c...
Các ngôn ng lp trình bc cao (high-level language) là phng tin giúp ngi làm tin
hc gii quyt các vn đ thc t nhng đng thi cng là ni mà nhng thành tu nghiên
cu mi nht ca khoa hc máy tính đc đa vào. Lnh vc nghiên cu phát trin các ngôn
ng lp trình va có tính truyn thng, va có tính hin đi. Ngày nay, vi nhng tin b ca
khoa hc công ngh, ngi ta đã có th s dng các công c hình thc cho phép gim nh
công vic lp trình t lúc phân tích, thit k cho đn s dng mt ngôn ng lp trình.
I.1.2. nh ngha mt ngôn ng lp trình
Các ngôn ng lp trình bc cao đc xây dng mô phng ngôn ng t nhiên, thng là
ting Anh (hoc ting Nga nhng nm trc đây). nh ngha mt ngôn ng lp trình là đnh
ngha mt vn phm (grammar) đ sinh ra các câu đúng ca ngôn ng đó. Có th hình dung
mt vn phm gm bn thành phn : b ký t, b t vng, cú pháp và ng ngha.
1. B ký t (character set)
Gm mt s hu hn các ký t (hay ký hiu) đc phép dùng trong ngôn ng. Trong các
máy tính cá nhân, ngi ta thng s dng các ký t ASCII. Có th hiu b ký t có vai trò
nh bng ch cái (alphabet) ca mt ngôn ng t nhiên đ to ra các t (word).
2. B t vng (vocabulary)
Gm mt tp hp các t, hay đn v t vng (token), đc xây dng t b ký t. Các t
dùng đ to thành câu lnh trong mt chng trình và đc phân loi tu theo vai trò chc
nng ca chúng trong ngôn ng. Chng hn chng trình Pascal sau đây :
program P;
var ×, y : integer;
begin
read(x);
y:=x+2;
write(y)
end.
gm các đn v t vng :
T khoá (keyword), hay t dành riêng (reserved word) : program, var, integer,
begin, end.
•
•
•
•
•
Tên, hay đnh danh (identifier) : read, write, P, x, y.
Hng (constants) : 2
Phép toán (operators) : + , :=
Du phân cách (delimiters) : :, (, ), ...
NGUYÊN LÝ LP TRÌNH HÀM 3
3. Cú pháp (syntax)
Cú pháp quy đnh cách thc kt hp các ký t thành t, kt hp các t thành câu lnh
đúng (statement hay instruction), kt hp các câu lnh đúng thành mt chng trình hoàn
chnh v mt vn phm. Có th hình dung cách kt hp này ging cách đt câu trong mt
ngôn ng t nhiên. Thng ngi ta dùng s đ cú pháp (syntax diagram) hoc dng chun
Backus-Naur (Backus-Naur Form, vit tt BNF), hoc dng chun Backus-Naur m rng
(EBNF − Extended Backus-Naur Form) đ mô t cú pháp ca vn phm.
Ví d I.1.1 : Trong ngôn ng Pascal (hoc trong phn ln các ngôn ng lp trình), tên gi,
hay đnh danh (identifier) có s đ cú pháp nh sau :
tên
ch
s
ch
s
0 ... 9
ch
A ... Z a ... z
Hình I.1. S đ cú pháp tên trong ngôn ng Pascal .
Trong mt s đ cú pháp, các ô hình ch nht ln lt phi đc thay th bi các ô hình
tròn. Quá trình thay th thc hin th t theo chiu mi tên cho đn khi nhn đc câu đúng.
Chng hn có th «đc» s đ trên nh sau : tên phi bt đu bng ch, tip theo có th là
ch hoc s tu ý, ch ch có th là mt trong các ch cái A..Za..z, s ch có th là mt trong
các ch s 0..9. Nh vy, Delta, x1, x2, Read, v.v... là các tên vit đúng, còn 1A, β, π,
bán kính, v.v... đu không phi là tên vì vi phm quy tc cú pháp.
Vn phm BNF gm mt dãy quy tcc. Mi quy tc gm v trái, du đnh ngha ::= (đc
đc đnh ngha bi) và v phi. V trái là mt ký hiu phi đc đnh ngha, còn v phi là
mt dãy các ký hiu, hoc đc tha nhn, hoc đã đc đnh ngha t trc đó, tuân theo
mt quy c nào đó. EBNF dùng các ký t quy c nh sau :
Ký hiu Ý ngha
::=, hoc →, hoc = đc đnh ngha là
{ } chui ca 0 hay nhiu mc lit kê tu chn (option)
[] hoc 0 hoc 1 mc lit kê tu chn
< > mc lit kê phi đc thay th
| hoc (theo ngha loi tr)
Các quy tc BNF đnh ngha tên trong ngôn ng Pascal :
<tên> ::= <ch> { <ch> | <s> }
<ch> ::= ’A’ | ... | ’Z’ | ’a’ | ... | ’z’
<s> ::= ’0’ | ... | ’9’
Ví d I.1.2
Vn phm ca mt ngôn ng lp trình đn gin dng EBNF nh sau :
<program> ::= program <statement>
*
end
<statement> ::= <assignment> | <loop>
<assignment> ::= <identifier> := <expression> ;
4 LP TRÌNH HÀM
<loop> ::=
while <expression> do <statement>+ done
<expression> ::=
<value> | <value> + <value> | <value> <= <value>
<value> ::= <identifier> | <number>
<identifier> ::=
<letter>|<identifier><letter>|<identifier><digit>
<number> ::= <digit> | <number><digit>
<letter> ::= ’A’ | ... | ’Z’ | ’a’ | ... | ’z’
<digit> ::= ’0’ | ... | ’9’
Mt câu, tc là mt chng trình đn gin, vit trong vn phm trên nh sau :
program
n := 1 ;
while n <= 10 do n := n + 1 ; done
end
4. Ng ngha (semantic)
Cn c vào cú pháp ca ngôn ng lp trình, ngi lp trình vit chng trình gm các câu
lnh theo trình t cho phép đ gii quyt đc bài toán ca mình. đt đc mc đích đó,
mi câu lnh vit ra không nhng đúng đn v mt cú pháp, mà còn phi đúng đn c v mt
ng ngha, hay ý ngha logic ca câu lnh. Tính đúng đn v mt ng ngha cho phép gii
quyt đc bài toán, chng trình chy luôn luôn dng, n đnh và cho kt qu phù hp vi
yêu cu đt ra ban đu.
I.1.3. Khái nim v chng trình dch
Chng trình đc vit trong mt ngôn ng lp trình bc cao, hoc bng hp ng, đu
đc gi là chng trình ngun (source program).
Bn thân máy tính không hiu đc các câu lnh trong mt chng trình ngun. Chng
trình ngun phi đc dch (translate) thành mt chng trình đích (target program) trong
ngôn ng máy (là các dãy s 0 và 1), máy mi có th đc «hiu» và thc hin đc. Chng
trình đích còn đc gi là chng trình thc hin (executable program).
Chng trình trung gian đm nhim vic dch đó đc gi là các chng trình dch.
Vic thit k chng trình dch cho mt ngôn ng lp trình đã cho là cc k khó khn và
phc tp. Chng trình dch v nguyên tc phi vit trên ngôn ng máy đ gii quyt vn đ
x lý ngôn ng và tính vn nng ca các chng trình ngun. Tuy nhiên, ngi ta thng s
dng hp ng đ vit các chng trình dch. Bi vì vic dch mt chng trình hp ng ra
ngôn ng máy đn gin hn nhiu. Hin nay, ngi ta cng vit các chng trình dch bng
chính các ngôn ng bc cao hoc các công c chuyên dng.
Thông thng có hai loi chng trình dch, hay hai ch đ dch, là trình biên dch và
trình thông dch, hot đng nh sau :
Trình biên dch (compilater) dch toàn b chng trình ngun thành chng trình đích
ri sau đó mi bt đu tin hành thc hin chng trình đích.
•
• Trình thông dch (interpreter) dch ln lt tng câu lnh mt ca chng trình ngun
ri tin hành thc hin luôn câu lnh đã dch đó, cho ti khi thc hin xong toàn b
chng trình.
Có th hiu trình biên dch là dch gi, trình thông dch là thông dch viên.
NGUYÊN LÝ LP TRÌNH HÀM 5
Nhng ngôn ng lp trình cp cao ch đ biên dch hay gp là : Fortran, Cobol, C,
C++, Pascal, Ada, Basic... ch đ thông dch hay ch đ tng tác : Basic,Lisp, Prolog...
I.1.4. Phân loi các ngôn ng lp trình
Cho đn nay, đã có hàng trm ngôn ng lp trình đc đ xut nhng trên thc t, ch có
mt s ít ngôn ng đc s dng rng rãi. Ngoài cách phân loi theo bc nh đã nói trên,
ngi ta còn phân loi ngôn ng lp trình theo phng thc (paradigm), theo mc đ quan
trng (measure of emphasis), theo th h (generation), v.v...
Cách phân loi theo bc hay mc (level) là da trên mc đ tru tng so vi các yu t
phn cng, chng hn nh lnh (instructions) và cp phát b nh (memory allocation).
Mc Lnh S dng b nh Ví d
Thp Lnh máy đn gin Truy cp và cp phát trc tip Hp ng, Autocode
Cao
Biu thc và điu khin
tng minh
Truy cp và cp phát nh các phép
toán, chng hn new
FORTRAN, ALGOL,
Pascal, C, Ada
Rt cao Máy tru tng Truy cp n và t đng cp phát
SELT, Prolog,
Miranda
Hình I.2. Ba mc ca ngôn ng lp trình.
Nhng nm gn đây, ngôn ng lp trình đc phát trin theo phng thc lp trình (còn đc gi
là phong cách hay kiu lp trình). Mt phng thc lp trình có th đc hiu là mt tp hp các tính
nng tru tng (abstract features) đc trng cho mt lp ngôn ng mà có nhiu ngi lp trình
thng xuyên s dng chúng. S đ sau đây minh ho s phân cp ca các phng thc lp trình :
Khai báo
Phng thc lp trình
Mnh lnh
C s
d liu
Hàm
Lôgic
X lý
song song
Hng
đi tng
Th tc
Hình I.3. Phân cp ca các phng thc lp trình.
Sau đây là mt s ngôn ng lp trình quen thuc lit kê theo phng thc :
Các ngôn ng mnh lnh (imperative) có Fortran (1957), Cobol (1959), Basic (1965),
Pascal (1970), C (1971), Ada (1979)...
•
•
•
•
•
•
Các ngôn ng đnh hng đi tng (object-oriented) có Smalltalk (1969), C
++
(1983), Eiffel (1986), Java (1991), C
#
(2000), ...
Các ngôn ng hàm (functional) có Lisp (1958), ML (1973), Scheme (1975), Caml
(1987), Miranda (1982), ...
Các ngôn ng da logic (logic-based) ch yu là ngôn ng Prolog (1970).
Ngôn ng thao tác c s d liu nh SQL (1980)...
Các ngôn ng x lý song song (parallel) nh Ada, Occam (1982), C-Linda, ...
Ngoài ra còn có mt s phng thc lp trình đang đc phát trin ng dng nh :
6 LP TRÌNH HÀM
Lp trình phân b (distributed programming). •
•
•
•
Lp trình ràng buc (constraint programming).
Lp trình hng truy cp (access-oriented programming).
Lp trình theo lung d liu (dataflow programming), v.v...
Vic phân loi các ngôn ng lp trình theo mc đ quan trng là da trên cái gì (what) s
thao tác đc (achieved), hay tính đc (computed), so vi cách thao tác nh th nào (how).
Mt ngôn ng th hin cái gì s thao tác đc mà không ch ra cách thao tác nh th nào
đc gi là ngôn ng đnh ngha (definitional) hay khai báo (declarative). Mt ngôn ng th
hin cách thao tác nh th nào mà không ch ra cái gì s thao tác đc gi là ngôn ng thao
tác (operational) hay không khai báo (non-declarative), đó là các ngôn ng mnh lnh.
Hình I.4. Phát trin ca ngôn ng lp trình.
Các ngôn ng lp trình cng đc phân loi theo th h nh sau :
Th h 1 : ngôn ng máy •
•
•
•
•
•
Th h 2 : hp ng
Th h 3 : ngôn ng th tc
Th h 4 : ngôn ng áp dng hay hàm
Th h 5 : ngôn ng suy din hay da logic
Th h 6 : mng n-ron (neural networks)
NGUYÊN LÝ LP TRÌNH HÀM 7
Trc khi nghiên cu lp các ngôn ng lp trình hàm, ta cn nhc li mt s đc đim
ca lp các ngôn ng lp trình mnh lnh.
I.1.5. Ngôn ng lp trình mnh lnh
Trong các ngôn ng mnh lnh, ngi lp trình phi tìm cách din đt đc thut toán,
cho bit làm cách nào đ gii mt bài toán đã cho. Mô hình tính toán s dng mt tp hp
(hu hn) các trng thái và s thay đi trng thái. Mi trng thái phn ánh ni dung các bin
d liu đã đc khai báo. Trng thái luôn b thay đi do các lnh điu khin và các lnh gán
giá tr cho các bin trong chng trình. Chng trình biên dch cho phép lu gi các trng
thái trong b nh chính và thanh ghi, ri chuyn các phép toán thay đi trng thái thành các
lnh máy đ thc hin.
Kiu :
tp hp giá tr
tp hp phép toán
cu trúc lu tr
bit
du
2
14
2
13
2
2
. . .
2
1
2
0
biu din theo bit :
0
0 0
1 . . .
0 1
Tên bin
i
Tên kiu
integer
..., −1, 0, 1, ...
+, −, ×, /, ...
Mc
ngôn ng
Mc
chng
trình
dch
S
5
Mc
máy
Hình I.5. Quan h gia tên bin, kiu và giá tr trong ngôn ng mnh lnh
Hình I.5. minh ha cách khai báo d liu trong các ngôn ng mnh lnh và các mi quan
h theo mc. Ngi ta phân bit ba mc nh sau : mc ngôn ng liên quan đn tên bin, tên
kiu d liu và cu trúc lu tr ; mc chng trình dch liên quan đn phng pháp t chc
b nh và mc máy cho bit cách biu din theo bit và giá tr d liu tng ng. Mi khai
báo bin, ví d int i, ni kt (bind) tên bin (i) vi mt cu trúc đc trng bi tên kiu
(int) và vi mt giá tr d liu đc biu din theo bit nh lnh gán i := 5 (hoc nh mt
lnh va khai báo va khi gán int i=5). T hp tên, kiu và giá tr đã to nên đc trng
ca bin.
Các ngôn ng mnh lnh đc s dng hiu qu trong lp trình do ngi lp trình có th
tác đng trc tip vào phn cng. Tuy nhiên, tính thc dng mnh lnh làm hn ch trí tu
ca ngi lp trình do phi ph thuc vào cu trúc vt lý ca máy tính. Ngi lp trình luôn
có khuynh hng suy ngh v nhng v trí lu tr d liu đã đc đt tên (nguyên tc đa ch
hoá) mà ni dung ca chúng thng xuyên b thay đi. Thc t có rt nhiu bài toán cn s
tru tng hoá khi gii quyt (ngha là không ph thuc vào cu trúc vt lý ca máy tính),
không nhng đòi hi tính thành tho ca ngi lp trình, mà còn đòi hi kin thc Toán hc
tt và kh nng tru tng hoá ca h.
T nhng lý do trên mà ngi ta tìm cách phát trin nhng mô hình tng tác không phn
ánh mi quan h vi phn cng ca máy tính, mà làm d dàng lp trình. Ý tng ca mô hình
là ngi lp trình cn đc t cái gì s đc tính toán mà không phi mô t cách tính nh th
nào. S khác nhau gia «nh th nào» và «cái gì», cng nh s khác nhau gia các ngôn ng
8 LP TRÌNH HÀM
mnh lnh và các ngôn ng khai báo, không phi luôn luôn rõ ràng. Các ngôn ng khai báo
thng khó cài đt và khó vn hành hn các ngôn ng mnh lnh. Các ngôn ng mnh lnh
thng gn gi ngi lp trình hn.
Sau đây là mt s đc trng ca ngôn ng lp trình mnh lnh :
− S dng nguyên lý tinh ch tng bc hay làm mn dn, x lý ln lt các đi tng
d liu đã đc đt tên.
− Khai báo d liu đ ni kt mt tên bin đã đc khai báo vi mt kiu d liu và mt
giá tr. Phm vi hot đng (scope) ca các bin trong chng trình đc xác đnh bi
các khai báo, hoc toàn cc (global), hoc cc b (local).
− Các kiu d liu c bn thông dng là s nguyên, s thc, ký t và lôgic. Các kiu mi
đc xây dng nh các kiu cu trúc. Ví d kiu mng, kiu bn ghi, kiu tp hp,
kiu lit kê,...
− Hai kiu d liu có cùng tên thì tng đng vi nhau, hai cu trúc d liu là tng
đng nu có cùng giá tr và có cùng phép toán x lý.
− Trng thái trong (b nh và thanh ghi) b thay đi bi các lnh gán. Trng thái ngoài
(thit b ngoi vi) b thay đi bi các lnh vào-ra. Giá tr đc tính t các biu thc.
− Các cu trúc điu khin là tun t, chn la (r nhánh), lp và gi chng trình con.
− Chng trình con thng có hai dng : dng th tc (procedure) và dng hàm
(function). S khác nhau ch yu là hàm luôn tr v mt giá tr, còn th tc thì không
không nht thit tr v giá tr. Vic trao đi tham bin (parameter passing) vi chng
trình con hoc theo tr (by value) và theo tham chiu (by reference).
− S dng chng trình con thng gây ra hiu ng ph (side effect) do có th làm thay
đi bin toàn cc.
− Mt chng trình đc xây dng theo bn mc : khi (block), chng trinh con, đn
th (module/packages) và chng trình.
I.2 C s ca các ngôn ng hàm
I.2.1. Tính khai báo ca các ngôn ng hàm
Trong các ngôn ng mnh lnh, mt chng trình thng cha ba li gi chng trình
con (th tc, hàm) liên quan đn quá trình đa vào d liu, x lý d liu và đa ra kt qu
tính toán nh sau :
begin
GetData(...) ; { đa vào }
ProcessData(...); { x lý }
OutPutResults(...); { xem kt qu }
end
Trong các ngôn ng lp trình hàm, các li gi chng trình con đc vit thành biu thc
rt đn gin :
(print
(process-data
(get-data (...))))
NGUYÊN LÝ LP TRÌNH HÀM 9
Các ngôn ng hàm là cng các ngôn ng bc cao, mang tính tru tng hn so vi các
ngôn ng mnh lnh.
Nhng ngi lp trình hàm thng tránh s dng các bin toàn cc, trong khi đó, hu ht
nhng ngi lp trình mnh lnh đu phi s dng đn bin toàn cc.
Khi lp trình vi các ngôn ng hàm, ngi lp trình phi đnh ngha các hàm toán hc d
suy lun, d hiu mà không cn quan tâm chúng đc cài đt nh th nào trong máy.
Nhng ngi theo khuynh hng lp trình hàm cho rng các lnh trong mt chng trình
vit bng ngôn ng mnh lnh làm thay đi trng thái toàn cc là hoàn toàn bt li. Bi vì rt
nhiu phn khác nhau ca chng trình (chng hn các hàm, các th tc) tác đng không trc
tip lên các bin và do vy làm chng trình khó hiu. Các th tc thng đc gi s dng
các phn khác nhau ca chng trình gi nên rt khó xác đnh các bin b thay đi nh th
nào sau li gi. Nh vy, s xut hin hiu ng ph làm cn tr vic chng minh tính đúng
đn (correctness proof), cn tr ti u hóa (optimization), và cn tr quá trình song song t
đng (automatic parrallelization) ca chng trình.
Mt ngôn ng hàm, hay ngôn ng áp dng (applicative language) da trên vic tính giá
tr ca biu thc đc xây dng t bên ngoài li gi hàm. đây, hàm là mt hàm toán hc
thun túy : là mt ánh x nhn các giá tr ly t mt min xác đnh (domain) đ tr v các giá
tr thuc mt min khác (range hay co-domain).
Mt hàm có th có, hoc không có, các tham đi (arguments hay parameters) đ sau khi
tính toán, hàm tr v mt giá tr nào đó. Chng hn có th xem biu thc 2 + 3 là hàm tính
tng (phép +) ca hai tham đi là 2 và 3.
Ta thy rng các hàm không gây ra hiu ng ph trong trng thái ca chng trình, nu
trng thái này đc duy trì cho các tham đi ca hàm. Tính cht này đóng vai trò rt quan
trng trong lp trình hàm. ó là kt qu ca mt hàm không ph vào thi đim (when) hàm
đc gi, mà ch ph thuc vào cách gi nó nh th nào đi vi các tham đi.
Trong ngôn ng lp trình mnh lnh, kt qu ca biu thc :
f(x) + f(x)
có th khác vi kt qu :
2 * f(x)
vì li gi f(x) đu tiên có th làm thay đi x hoc mt bin nào đó đc tip cn bi f. Trong
ngôn ng lp trình hàm, c hai biu thc trên luôn có cùng giá tr.
Do các hàm không ph thuc nhiu vào các bin toàn cc, nên vic lp trình hàm s d
hiu hn lp trình mnh lnh. Ví d gi s mt trình biên dch cn ti u phép tính :
f(x) + f(x)
thành :
2 * f(x)
Khi đó, trình biên dch mt ngôn ng hàm luôn luôn xem hai kt qu là mt, do có tính
nht quán trong kt qu tr v ca hàm. Tuy nhiên, mt trình biên dch ngôn ng mnh lnh,
ngôn ng Ada
1
chng hn, thì đu tiên phi chng minh rng kt qu ca li gi th hai
không ph thuc vào các bin đã b thay đi trong quá trình thc hin bi li gi th nht.
1
Ada là ngôn ng lp trình bc cao đc phát trin nm 1983 bi B Quc phòng M (US Department of
Defense), còn gi là Ada 83, sau đó đc phát trin bi Barnes nm 1994, gi là Ada 9X. Ngôn ng Ada ly tên
ca nhà n Toán hc ngi Anh, Ada Augusta Lovelace, con gái ca nhà th Lord Byron (1788−1824). Ngi
ta tôn vinh bà là ngi lp trình đu tiên.
10 LP TRÌNH HÀM
Mt trình biên dch song song s gp phi vn đ tng t nu trình này mun gi hàm
theo kiu gi song song.
Bên cnh tính u vit, ta cng cn xem xét nhng bt li vn có ca lp trình hàm :
nhc đim ca ngôn ng hàm là thiu các lnh gán và các bin toàn cc, s khó khn trong
vic mô t các cu trúc d liu và khó thc hin quá trình vào/ra d liu.
Tuy nhiên, ta thy rng s thiu các lnh gán và các bin toàn cc không nh hng hay
không làm khó khn nhiu cho vic lp trình. Khi cn, lnh gán giá tr cho các bin đc mô
phng bng cách s dng c cu tham bin ca các hàm, ngay c trong các chng trình vit
bng ngôn ng mnh lnh.
Chng hn ta xét mt hàm P s dng mt bin cc b x và tr v mt giá tr có kiu bt
k nào đó (SomeType). Trong ngôn ng mnh lnh, hàm P có th làm thay đi x bi gán
cho x môt giá tr mi. Trong mt ngôn ng hàm, P có th mô phng s thay đi này bi
truyn giá tr mi ca x nh là mt tham đi cho mt hàm ph tr thc hin phn mã còn li
ca P. Chng hn, s thay đi giá tr ca bin trong chng trình P :
function P(n: integer) −> SomeType ;
x: integer := n + 7
begin
x := x * 3 + 1
return 5 * g(x)
end ;
ta có th vit li nh sau :
function P(n : integer) −> SomeType ;
x: integer := n + 7
begin
return Q(3*x + 1) % mô phng x := x * 3 + 1
end ;
trong đó, hàm mi Q đc đnh ngha nh sau :
function Q(x: integer) −> Some Type
begin
return 5 * g(x)
end ;
Ta cng có th s dng k thut này cho các bin toàn cc. Nh vy, vic mô phng lp
trình mnh lnh trong mt ngôn ng hàm không phi là cách mong mun, nhng có th làm
đc.
Mt vn đ ni bt trong ngôn ng hàm là s thay đi mt cu trúc d liu. Trong ngôn
ng mnh lnh, s thay đi mt phn t ca mt mng rt đn gin. Trong ngôn ng hàm,
mt mng không th b thay đi. Ngi ta phi sao chép mng, tr ra phn t s b thay đi,
và thay th giá tr mi cho phn t này. Cách tip cn này kém hiu qu hn so vi phép gán
cho phn t.
Mt vn đ khác ca lp trình hàm là kh nng hn ch trong giao tip gia h thng
tng tác vi h điu hành hoc vi ngi s dng. Tuy nhiên hin nay, ngi ta có xu
hng tng cng th vin các hàm mu x lý hng đi tng trên các giao din đ ho
(GUI-Graphic User Interface). Chng hn các phiên bn thông dch h Lisp nh DrScheme,
MITScheme, WinScheme...
NGUYÊN LÝ LP TRÌNH HÀM 11
Tóm li, ngôn ng hàm da trên vic tính giá tr ca biu thc. Các bin toàn cc và phép
gán b loi b, giá tr đc tính bi mt hàm ch ph thuc vào các tham đi. Thông tin trng
thái đc đa ra tng minh, nh các tham đi ca hàm và kt qu.
Sau đây ta s xét nhng khái nim đc coi là c bn nht trong các ngôn ng hàm : hàm
(function), danh sách (lists), kiu (type), tính đa kiu (polymorphism), các hàm bc cao
(higher−order functions), tham đi hóa tng phn (Currying), tính hàm theo kiu khôn
ngoan
2
(lazy evaluation), phng trình (equations), so khp (patterm matching).
Các hàm đ quy (recursive functions) là mt trong nhng khái nim chính trong ngôn ng
hàm. Các hàm bc cao và phng pháp tính hàm theo kiu khôn ngoan to th mnh cho lp
trình hàm và đóng vai trò quan trng trong vic xây dng các chng trình hàm dng đn th
(modular functional programs). Tính đa kiu b sung tính mm do cho h thng đnh kiu.
Trc khi tìm hiu ngôn ng Scheme bt đu t chng 2, cun sách s dng ngôn ng
Miranda đ trình bày nhng khái nim c bn ca lp trình hàm. Miranda là mt ngôn ng
hàm có cú pháp d đc, d hiu do David Turner phát trin nm 1986. c đim ca Miranda
là thun tuý hàm (purely functional), không xy ra hiu ng ph. Mt chng trình Miranda,
đc gi là mt «script», là mt tp hp các phng trình (equation) đc đnh ngha theo
mt th t tu ý nào đó (nói chung th t không quan trng).
Trình thông dch Miranda chy ch đ tng tác trong h điu hành Unix. Sau đây, chúng
tôi không trình bày đy đ cú pháp ca Miranda mà ch qua các ví d ca Miranda đ minh
ho các yu t thun tuý hàm ca lp trình hàm.
Ví d sau đây la mt chng trình Miranda đn gin. Chú ý cp ký hiu || đ bt đu
mt dòng chú thích ca Miranda.
z = sq x / sq y || z = sq(x)/sq(y) = x
2
/y
2
sq n = n * n || sq(n) = n
2
x = a + b
y = a - b
a = 10
b = 5
I.2.2. nh ngha hàm
Hàm là khái nim c bn trong các ngôn ng hàm. Mt hàm có th nhn t không đn
nhiu tham đi vào đ tính toán và tr v mt giá tr, giá tr này ch ph thuc vào các tham
đi đã nhn mà thôi.
Trong Miranda, mt hàm đc đnh ngha bi hai phn : phn khai báo và phn đnh
ngha hàm. Phn khai báo có th vng mt có dng mt nguyên mu hàm :
<tên hàm> :: <min xác đnh> -> <min giá tr>
Phn đnh ngha hàm có dng mt phng trình, gm v trái và mt s v phi, mi v
phi có th có mt điu kin đóng vai trò «lính gác» (guard) phân bit đng cui :
<tên hàm> [ <danh sách tham đi> ] = <biu thc> [ <điu kin> ]
Ví d hàm đi nhit đ t đ Fahrenheit (F) sang đ Celsius (C) đc đnh ngha trong
Miranda nh sau :
celsius :: num −> num || khai báo kiu hàm
celsius f = (f − 32)*5/9 || đi t đ Fahrenheit sang đ Celsius
2
Trong cun sách có nhiu thut ng ting Vit do tác gi t dch t ting Anh.
12 LP TRÌNH HÀM
Dòng đu tiên khai báo celsius là mt hàm nhn mt đi s và tr v mt giá tr s.
Dòng th hai tính đi nhit đ. Áp dng hàm celsius đã đnh ngha cho các đi s c th,
ta có :
celsius 68
--> 20
celsius (-40)
--> -40
đây, ký hiu --> dùng đ ch rng giá tr tr v ca hàm là nhng gì theo sau ký hiu
này. Chng hn, li gi celsius 68 tr v giá tr 20.
Ngôn ng Miranda s dng cú pháp ngn gn và không s dng các cp du ngoc đ
bao bc các tham đi hoc bao bc các li gi hàm.
Các hàm đ quy đóng vai trò quan trng trong các ngôn ng hàm. Chng hn, xét mt ví
d c đin là tính c s chung ln nht gcd (greatest common divisor) theo thut toán
Euclide :
gcd a b = gcd (a-b) b, if a > b
= gcd a (b-a), if a < b
= a, if a=b || hoc otherwise
Hàm gcd đc đnh ngha nh phng trình có 3 v phi phân bit, mi v phi cha
mt điu kin (a > b, a < b và a=b hay otherwise). Miranda s thc hin gi đ quy
cho đn khi gp điu kin dng là a=b và cng là kt qu tính gcd. Ta thy các lính gác
trong phng trình thc hin tng t lnh if trong ngôn ng mnh lnh.
Trong các giáo trình nhp môn Tin hc, ngi ta thng ly ví d tính giai tha ca mt
s nguyên không âm bng phng pháp đ quy. Ta s thy ngôn ng Miranda tính giai tha
kiu đ quy nh sau :
fac :: num −> num
fac n = 1, if n = 0 || fac 0 = 1
fac x = x * fac (x - 1), otherwise || li gi đ quy
so sánh, chng trình di đây ch ra cách tính n! trong mt ngôn ng mnh lnh
nhng không dùng đ quy (ch dùng đ quy khi tht cn thit !).
function fac (n: integer) : integer ;
var i, r: integer ; % r cha kt qu
begin
r := 1
{ tính n * (n - 1) * (n - 2) * ... * 1 : }
for i :=n downto 1 do r := r * i;
return r
end;
Hàm fac trên đây s dng mt bin trng thái r b thay đi giá tr trong vòng lp for.
Còn chng trình Miranda không s dng vòng lp for, mà s dng mt li gi đ quy. Mi
li gi đ quy cho fac tng ng vi mt ln gp ca vòng lp for. Do tính ngn gn ca
li gi đ quy trong ngôn ng hàm, nhiu ngi ng h lp trình hàm quan nim rng phép
lp trong các ngôn ng mnh lnh là rc ri, làm ngi đc khó hiu, khó theo dõi.
Sau đây là mt ví d khác tính nghim mt phng trình bc hai ax
2
+bx+c=0 :
NGUYÊN LÝ LP TRÌNH HÀM 13
quadsolve a b c
= error "complex roots", if delta<0
= [-b/(2*a)], if delta=0
= [-b/(2*a)] + radix/(2*a),
= [ -b/(2*a) - radix/(2*a)], if
where delta = b*b - 4*a*c and radix = sqrt delta
Mnh đ ca Miranda có th lng nhau (nested) nhiu mc.
I.2.3. Danh sách
Trong hu ht các ngôn ng hàm, danh sách (list) là cu trúc d liu quan trng nht.
Trong ngôn ng Miranda, mt danh sách gm t 0 đn nhiu phn t có cùng kiu, ví d :
[1, 4, 9, 16] || danh sách gm 4 s nguyên
[1..10] || danh sách 10 s nguyên (1..10)
[’u’, ’f’, ’0’] || danh sách gm 3 ký t
[] || danh sách rng (empty list) không có phn t nào
[[2, 4, 6], [0, 3, 6, 9]] || danh sách cha hai danh sách các s
nguyên
week_days = ["Mon","Tue","Wed","Thur","Fri"]
Các danh sách có th lng nhau (bao hàm nhau). Mt đim mnh ca Miranda và ca mt
s ngôn ng hàm khác là cho phép đnh ngha mt danh sách nh tính cht d nhn bit ca
các phn t ca nó, tng t cách xác đnh mt tp hp trong Toán hc. Mt danh sách đc
đnh ngha nh vy đc gi là nhn bit đc hay ngm hiu (list comprehension). Ví d,
cách vit :
[ n | n <- [1..10] : n mod 2 = 1]
--> [1, 3, 5, 7, 9]
ch đnh mt danh sách có th t ca các s l gia 1 và 10. Danh sách đc hiu là «gm tt
c các s n nm gia 1 và 10 tho mãn n mod 2 bng 1». Mt ví d khác v danh sách nhn
bit đc :
[ n*n | n <- [1..100]] || danh sách bình phng các s gia 1 và 100
Dng tng quát ca danh sách nhn bit đc :
[ body | qualifiers ]
trong đó mi qualifier hoc là mt b to sinh phn t (generator) có dng nh sau :
var <- exp
ký hiu <− biu din phn t var thuc (∈) mt tp hp ch đnh bi exp, hoc là mt b
lc có dng là mt biu thc lôgic thu hp min xác đnh ca các bin đc to ra bi b to
sinh. Khi có nhiu thành phn qualifier cùng xut hin thì chúng đc lit kê cách nhau
mt du : (du hai chm).
Ví d sau đây tr v danh sách các hoán v ca mt danh sách đã cho :
perms [] = [[]]
perms L = [ a:T | a <- L : T <- perms (L--[a]) ]
Ta có th đnh ngha li hàm tính giai tha nh hàm nhân product trong th vin ca
Miranda :
14 LP TRÌNH HÀM
fac n = product [1..n]
Tng t, ta đnh ngha hàm tính tng các s l gia 1 và 100 nh hàm sum trong th
vin ca Miranda :
result = sum [1, 3..100]
Ngi ta xây dng nhiu phép toán khác nhau trên các danh sách. Tùy theo ngôn ng
hàm, mt s phép toán có th có sn (build-in), mt s phép toán khác do ngi s dng t
đnh ngha. Các phép toán s cp nht là xây dng (construction) và phân tách
(decomposing) các danh sách. Chng hn phép toán hd (head) ly ra phn t đu tiên ca
mt danh sách đ tr v mt phn t. Phép toán tl (tail) tr v danh sách còn li (sau khi đã
ly đi phn t đu tiên).
hd [10, 20, 30]
--> 10
tl [10, 20, 30]
--> [20, 30]
Phép toán : (cons) đ chèn mt phn t vào trc các phn t trong mt danh sách :
5 : []
--> [5]
3 : [4, 5, 6]
--> [3, 4, 5, 6]
0 : [1, 2, 3]
--> [0,1, 2, 3]
Các phép toán khác là : phép # (du # đt trc danh sách) đm s phn t là đ dài
(length) mt danh sách, phép ghép (concatenate) ++ (hai du cng liên tip) các danh sách,
phép ! ly t danh sách ra mt s phn t (infix), hàm nghch đo reverse mt danh sách,
v.v...
[1, 2, 3] ++ [4, 5]
--> [1, 2, 3, 4, 5] || ghép hai danh sách
days = week_days ++ ["Sat","Sun"]
days
--> ["Mon", "Tue", "Wed", "Thur", "Fri", "Sat", "Sun"]
# [3..7]
--> 5 || đ dài mt danh sách
#days
--> 7
days!0
--> "Mon"
Miranda còn có phép -- (hai du tr liên tip) tính hiu ca hai tp hp là hai danh sách :
[1,2,3,4,5] -- [2,4]
--> [1,3,5]
hiu rõ hn các lp trình trong Miranda, sau đây ta s vit li mt s hàm th vin ca
Miranda. Gi s ta vit hàm length tính đ dài nh sau :
NGUYÊN LÝ LP TRÌNH HÀM 15
length L = 0, if L = [] || danh sách rng có đ dài 0
|| danh sách khác rng có ít nht mt phn t
length L = 1 + length (tl L), otherwise
Tng t, hàm concatenate ghép hai danh sách đc xây dng nh sau :
concatenate L1 L2 = L2, if L1 = []
concatenate L1 L2
= (hd L1):concatenate (tl L1) L2, otherwise
Hàm concatenate đc trng cho kiu lp trình đ quy ca các ngôn ng hàm. Dòng
đu tiên ch cách thoát ra khi hàm, theo kiu chia đ tr (divide and conquer). Trong trng
hp ghép mt danh sách rng (if L1 = []) vi mt danh sách bt k khác (L2) thì tr v
kt qu chính là danh sách này và quá trình kt thúc.
Dòng th hai là trng hp tng quát v ghép hai danh sách thành mt danh sách mi s
dng phép toán cons (otherwise) theo kiu đ quy. Do danh sách th nht L1 ≠ [] nên
L1 có ít nht mt phn t. Khi đó danh sách mi (là danh sách kt qu) đc to ra bi phn
t đu tiên ca L1, là (hd L1) ghép vi (phép :) mt danh sách còn li. Danh sách còn li
này li là kt qu ca bài toán ban đu nhng nh hn vi tham đi th nht là danh sách còn
li ca L1 và tham đi th hai là toàn b L2. Quá trình c th tip tc cho đn khi danh sách
còn li rng.
Ta có th so sánh cách xây dng các danh sách trong các ngôn ng mnh lnh vi ngôn
ng Miranda.
Trong các ngôn ng mnh lnh, danh sách do ngi lp trình t xây dng thng theo
kiu móc ni s dng bin con tr (pointer) và s dng b nh cp phát đng (dynamic
allocation). Các phép toán trên danh sách là s thao tác mc thp trên các con tr. Vic cp
phát và gii ta b nh cng do ngi s dng t gii quyt. Thông thng, ngi lp trình
có xu hng s dng vic cp nht phá hy (destructive updates) trên các cu trúc d liu.
Ví d, ngi lp trình Ada ghép danh sách bng cách vit mt th tc cho phép móc ni
(hook) hai danh sách vi nhau nh mt phép gán con tr, mà không cn xây dng mt danh
sách mi (th ba). Tuy nhiên tip cn kiu Ada có th gây ra hiu ng ph nu làm thay đi
danh sách th nht.
Trong các ngôn ng hàm, ngi lp trình không h quan tâm đn các con tr và vic cp
phát b nh. Mc du nhng yu t này là tn ti trong ngôn ng hin hành, nhng chúng
đc che du mt cách hiu qu đi vi ngi lp trình to nên mt mc tru tng có li
cho ngi lp trình, giúp h tránh đc nhng sai sót khi lp trình.
Trong nhng tình hung nh vy, mt h thng thi gian thc hin RST (Run Time
System) qun lý b nh lu tr các phn t ca danh sách. RTS cho phép cp phát b nh khi
cn thit và t đng phát hin nhng khi nh không còn cn dùng đn na đ gii phóng
chúng.
Ngôn ng Miranda còn có kiu d liu tuple tng t kiu bn ghi ca Pascal. ó là
mt dãy phn t không có cùng kiu (danh sách là mt dãy phn t có cùng kiu tng t
kiu mng array) đc vit gia mt cp du ngoc. Ví d :
employee = ("Kim",True, False , 29)
Các phn t kiu tuple không có th t. Vic truy cp đn mt phn t ch có th đc
thc hin nh phép so khp (pattern matching).
16 LP TRÌNH HÀM
I.2.4. Phép so khp
Nh đã thy, mt hàm trong Miranda có th đc đnh ngha bng cách s dng nhiu
biu thc v phi khác nhau, theo sau là «lính gác», đó là các điu kin nh if x>0,
otherwise hay while. Tuy nhiên, Miranda cho phép s dng mt cú pháp tng quát đ
la chn gia các kh nng da trên các mu (patterns) dng :
<patterm> = <expression>, <condition>
trong đó, phn <condition> là tùy chn. Mu có th s dng các tham bin hình thc, các
hng và mt s cu trúc khác. Khi mt hàm đc gi, li gi hàm gm tên hàm và các tham
đi thc s ca nó (nu có) s đc so khp vi các mu trong chng trình. Nu so khp
thành công, dòng lnh tng ng vi mu đc thc hin. Nu có nhiu mu đc tìm thy,
mu đu tiên (theo th t vit các lnh) s đc chn.
Sau đây là mt s ví d đn gin minh ha cách đnh ngha các hàm s dng phép so
khp. Hàm cond đã xét có th vit di dng :
cond true x y = x
cond false x y = y
Hàm fac có th vit vi hai dòng chng trình :
fac 0 = 1
fac (n+1) = (n+1) * fac n
Phép so khp th nht xy ra nu tham đi bng 0. Mu th hai đc so ch khi nu tham
đi là ln hn hoc bng 1. Khi đó giá tr ca n đc ly giá tr ca tham đi tr đi 1 mt
cách «khôn ngoan».
Trng hp tng quát, mt biu thc s xut hin trong mt mu có th có dng V + C,
vi V là mt bin và C là mt trc hng (literal constant). Mu s ch đc so nu V có th
nhn mt giá tr không âm. Hàm ackerman đc đnh ngha nh sau :
ack 0 n = n+1
ack (m+1) 0 = ack m 1
ack (m+1) (n+1) = ack m(ack (m+1) n)
Hàm tính s fibonacci th n :
fib 0 = 0
fib 1 = 1
fib (n+2) = fib (n+1) + fib n
Chú ý : Các biu thc s tng quát không th đc ly làm các mu đ tránh nhp nhng
(ambiguities). Ví d, mu :
f (n + m) = n * m
là nhp nhng và không đúng. Mt li gi hàm, f 9 chng hn, s gây ra kt qu hoc 1*8
hoc 2*7, hoc mt t hp khác tu ý.
Mu cng có th cha danh sách. Ví d, hàm tính đ dài ca mt danh sách đã cho trc
đây có th đc vit li di dng nh sau :
length [] = 0
length (a : L) = 1 + (length L)
Mu xut hin trong dòng th nht tng ng vi danh sách rng, ch có danh sách rng
mi th đc so khp đ có kt qu 0. Dòng th hai x lý danh sách khác rng. Trong trng
hp này, mt li gi đ quy đc to ra. Bin a đc khp vi phn t đu tiên ca danh
NGUYÊN LÝ LP TRÌNH HÀM 17
sách tham đi và do đó, L là danh sách còn li tr thành tham đi mi ca hàm. Kt qu li
gi đ quy đã gim đi 1.
Mt s hàm x lý danh sách khác s dng mu so khp : tính tng, tích và nghch đo
mt danh sách :
sum [] = 0
sum (a:L) = a + sum L
product [] = 1
product (a:x) = a * product x
reverse [] = []
reverse (a:L) = reverse L ++ [a]
truy cp đn mt phn t ca kiu bn ghi (tuple), s dng so khp, chng hn bn ghi
có hai phn t :
fst (a, b) = a || truy cp đn mt phn t th nht
snd (a, b) = b || truy cp đn mt phn t th hai
Sau đây đnh ngha li hai hàm th vin ca Miranda : hàm take tr v danh sách n
phn t đu tiên ca danh sách đã cho và hàm drop tr v phn còn li ca danh sách sau
khi đã loi b n phn t đu tiên.
take 0 L = []
take (n+1) [] = []
take (n+1) (a:L) = a : take n L
drop 0 L = L
drop (n+1) [] = []
drop (n+1) (a:L) = drop n L
Chú ý rng hai hàm đc đnh ngha sao cho đng nht thc sau đây tho mãn (bao gm
c trng hp oái om là đ dài ca L nh thua n) :
take n L ++ drop n L = L
I.2.5. Phng pháp currying (tham đi hoá tng phn)
Mt yu t quan trng khác ca hu ht các ngôn ng hàm là phng pháp currying (ly
tên nhà logic hc Haskell B. CURRY.), hay còn đc gi là phng pháp tham đi hoá tng
phn (partial parametrization).
Thông thng mt hàm đc thc hin theo kt hp trái, nu vit f x y (hàm f tác đng
lên hai đi x y), vit quy c (x, y)
→
f(x, y), thì cng đc xem nh vit (f x) y, ngha là kt
qu ca vic áp dng f cho x là mt hàm đ áp dng cho y. Ta vit x
→
(y
→
f (x, y)).
Mt cách tng quát cho hàm n bin f (x
1
, x
2
,..., x
n
) :
x
1
→
(x
2
→
(x
3
→
...(x
n
→
f (x
1
, ...,x
n
)) ...))
Chng hn xét hàm mult đc đnh ngha nh sau :
mult x y = x * y
Nu hàm mult đc gi vi hai đi s, thì mult s tính tích s ca hai đi s này, theo
ngha thông thng. Tuy nhiên trong ngôn ng Miranda, mult có th đc xem nh hàm
mt tham đi (đi th nht x), kt qu s là mt hàm khác, áp dng cho mt tham đi (đi
th hai y).
18 LP TRÌNH HÀM
Mt cách tng quát, mt hàm nào đó có nhiu hn mt tham đi có th đc tham đi
hóa tng phn. Chng hn ta đnh ngha hàm triple đ nhân 3 mt s :
triple x = 3 * x
Nhng cng có th đnh ngha li hàm triple theo cách currying :
triple = mult 3
Li gi :
triple 7
cho kt qu là 21 vì dn đn li gi mult 3 7.
I.2.6. Khái nim v bc ca hàm
Trong phn ln các ngôn ng mnh lnh, các đi tng c s (bin và hng) đc x lý
khác vi hàm. Chúng có th đc đc vào (t bàn phím, t tp...), đa ra (màn hình, máy in,
tp...), trao đi giá tr vi nhau, nhng hàm ch có th đc gi hoc đc kích hot (invoke).
Các đi tng c s đc gi là có bc 0, các hàm có các tham đi là nhng đi tng c
s đc gi là các hàm bc 1. Mt hàm bc n nhn các tham đi bc nh hn n, đ có các
biu thc bc cao hn.
Khái nim v bc đc Russel (Russell's Paradox) và Whitehead (Principia Mathematica
~1900) đ xut nhm loi b nhng nghch lý (paradox) thng hay gp trong lý thuyt tp hp
ca Cantor, chng hn nghch lý v ngi th co (barber paradox), bng cách làm gim tt c
các kh nng xy ra vòng lun qun. Trong lôgic v t bc mt, ch có th s dng các lng t
áp dng cho các cá th (các bin). Còn trong lôgic v t bc hai, ngi ta có th lng t hoá
các v t...
Trong các ngôn ng hàm, hàm luôn luôn đc x lý nh nhng đi tng đc truyn
tham đi đ tr v kt qu và đc lu gi trong các cu trúc d liu. Mt hàm nhn mt hàm
khác nh là mt tham đi đc gi là hàm bc cao. Các hàm bc cao mang li tính hiu qu
và là nn tng (corner-stone) ca lp trình hàm.
hiu bn cht ca mt hàm bc cao, ngi đc có th t tìm giá tr kt qu ca
answer t các lnh Miranda sau đây :
answer = twice twice twice suc 0
twice f x = f(f x)
suc x = x + 1
Ngôn ng Miranda luôn xem nhng hàm có nhiu hn mt tham đi đu là các hàm bc
cao. Chng hn t hàm th vin member x L kim tra danh sách L có cha phn t x
không (kt qu là true hoc false), ta có th áp dng đ đnh ngha các hàm khác nhau
nh sau :
vowel = member ['a', 'e', 'i', 'o', 'u']
digit =
member ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
month =
member [ "Jan", "Feb", "Mar", "Apr", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec" ]
Các ngôn ng hàm đu có phép toán «bao qua phi» (foldr right) dùng đ tính toán trên
các phn t ca mt danh sách. Miranda có hàm foldr đc đnh ngha nh sau :
NGUYÊN LÝ LP TRÌNH HÀM 19
foldr op k [] = k
foldr op k (a:L) = op a (foldr op k L)
Li gi hàm foldr :
foldr (+) 0 [2, 4, 7]
tng đng vi :
(2 + (4 + (7 + 0)))
--> 13
Chú ý các phép toán trung t trong ngôn ng Miranda, nh phép cng + trên đây, đc
xem là tham đi và phi đc đt trong mt cp du ngoc.
Ta có th đnh ngha các hàm x lý danh sách s dng hàm foldr. Chng hn hàm tính
tng tt c các phn t ca mt danh sách theo kiu currying nh sau :
sumlist = foldr (+) 0
Hàm sumlist có mt tham đi là danh sách s cung cp ba tham đi cho hàm foldr
nhng kt qu là do hàm op có mt tham đi tr v.
sumlist [2, 9, 52]
--> foldr (+) 0 [2, 9, 52]
--> 63
Mt cách tng t ta đnh ngha hàm tính tích ca tt c các phn t trong mt danh
sách :
product = foldr (*) 1
Hàm :
and = foldr (&) true
gi phép « và» lôgích cho tt c các phn t ca mt danh sách. Kt qu phép gi hàm and
L là true nu mi phn t ca danh sách L đu là true, kt qu là false nu ngc li.
Hàm nghch đo mt danh sách :
reverse = foldr postfix []
where postfix a L = L ++ [a]
Các ngôn ng hàm có nhiu hàm bc cao tin đnh, và ngi lp trình cng có th t đnh
ngha nhng hàm bc cao mnh và tng quát nh hàm foldr va xét.
Mt hàm bc cao quan trng khác là map f L vi f là mt hàm và L là mt danh sách.
Hàm map tr v kt qu là mt danh sách mi gm các phn t ca L đã đc áp dng cho
hàm f. Ví d :
map triple [1, 2, 3, 4]
--> [triple 1, triple 2, triple 3, triple 4]
--> [3, 6, 9, 12]
Hàm map gi triple cho tt c các phn t ca danh sách [1, 2, 3, 4], đ tr v
danh sách các s nguyên đã đc nhân ba là [3, 6, 9, 12].
20 LP TRÌNH HÀM
I.2.7. Kiu và tính đa kiu
Các ngôn ng hàm có mt h thng kiu d liu hoàn toàn khác vi các ngôn ng mnh
lnh. Trong các ngôn ng mnh lnh (nh Pascal, C, Ada..), tính đnh kiu tnh cht ch
(static strong typing) bt buc ngi lp trình phi mô t kiu cho mi bin dùng đn. Sau khi
khai báo, ngi s dng không đc thay đi kiu d liu ca bin trong khi chy chng
trình.
Các ngôn ng hàm thng không s dng đnh kiu. Tuy nhiên mt s ngôn ng hàm li
s dng đnh kiu đng (dymamic typing) hoc đnh kiu n cht ch (implicit strong typing)
(thng gp trong ngôn ng Fortran).
Nh vy, các ngôn ng hàm to ra đc tính mm do v các hàm đa kiu (polymorphic
functions) vi li gi có các tham đi có các kiu d liu khác nhau.
Miranda s dng ch đ đnh kiu tnh cht ch tng minh (implicit static strong
typing). Mi bin mi biu thc đu có mt kiu đc xác đnh tnh. Có ba kiu tin đnh
trong Miranda là num, bool và char. Kiu num không phân bit s nguyên hay s thc.
Kiu bool có hai giá tr hng là true và false. Kiu char gm các ký t ASCII. Mt
hng ký t đc đt gia cp du nháy đn và s dng quy c tng t ngôn ng C, nh
’x’, ’&’, ’\n’, v.v...
Nu T là mt kiu, thì [T] là mt kiu danh sách gm các phn t kiu T. Ví d danh
sách sau đây :
[[1, 2], [2, 3], [4, 5]]
có kiu [[num]] vì đây là mt danh sách gm các phn t là danh sách các s. Mt hng
chui có dng [ char ], chng hn chui «bonjour» đc vit trong Miranda [’b’,
’o’, ’n’, ’j’, ’o’, ’u’, ’r’].
Nu T1, ..., Tn là các kiu nào đó, n>0, thì (T1, ..., Tn) là mt kiu bn ghi, hay b n
phn t bt k. Ví d b 3 (true, ”Honda”, 7331) có kiu (bool, [char], num).
Nu T1 và T2 là hai kiu nào đó, thì T1->T2 là mt kiu hàm vi tham đi có kiu T1
và giá tr tr v có kiu T2. Ví d hàm triple va đnh ngha trên đây có kiu :
num −> num
Chú ý phép −> có kt hp phi (right associative).
Ngi lp trình có th s dng các khai báo đnh ngha hàm. Ví d, hàm mult đc khai
báo kiu currying nh sau :
mult :: num −> (num −> num)
Có ngha rng mult đc xem là hàm có mt tham đi tr v mt hàm khác có kiu :
num −> num.
Du :: trong khai báo hàm đc đc «có kiu là » (is of type). Ví d :
sq :: num -> num
sq n = n * n
Tuy nhiên, vic khai báo kiu hàm là không cn thit. Các ngôn ng hàm thng có kh
nng uy din kiu t đng nh mt b kim tra kiu (type checker). Khi không khai báo kiu
hàm, chng hn nu ch đnh ngha hàm triple bi :
triple x = 3 * x
NGUYÊN LÝ LP TRÌNH HÀM 21
thì b kim tra kiu s suy ra rng x phi là mt s, vì x là mt tha s trong mt phép nhân.
B kim tra kiu cng suy ra đc rng kt qu ca triple x phi là mt s, do đó kiu
ca hàm này phi là :
triple :: num −> num
Tính đa kiu là yu t rt quan trng trong nhiu ngôn ng hàm. Vi mi tham bin hình
thc (formal parameters), mt hàm đa kiu có th chp nhn li gi tng ng vi nhiu
tham đi thc s (actual parameters) có các kiu khác nhau. Khác vi các th tc trong Ada,
mt hàm đa kiu là mt hàm đn (single function), không phi là các hàm bi (multiple
functions) có cùng tên. Ví d xét hàm đa kiu pair sau đây tr v mt danh sách t hai tham
đi là hai phn t :
pair x y = [x, y]
Hàm này có th đc s dng nh sau :
pair 1 2
--> [1, 2]
pair true false
--> [true, false]
pair [] [2]
--> [[], [2]]
Hàm pair đc đnh ngha đn, nhng có th s dng nhiu kiu tham đi khác nhau,
nh bool, num, [num], v.v..., sao cho c hai tham đi đu phi có cùng mt kiu. Nh vy,
kiu ca hàm pair không th đc din t nh kiu ca hàm triple va đnh ngha
trên, vì rng các kiu ca các tham đi trong pair là không c đnh. Ngi ta đa ra gii
pháp s dng các bin kiu đc thù (generic type variables). Bên trong mt khai báo kiu,
mt bin kiu có th ch đnh mt kiu nào đó, cùng mt bin kiu cho phép ch đnh cùng
mt kiu xuyên sut c khai báo.
Miranda s dng các ký hiu *, **, *** đ ch đnh kiu tu ý. Ví d hàm đng nht id
(identity) đc đnh ngha nh sau :
id x = x
Hàm id có khai báo kiu nh sau :
id :: * -> *
có ngha là id có nhiu kiu. Sau đây là mt s khai báo kiu ca các hàm đã đc đnh
ngha trên đây :
fac :: num -> num
ack :: num -> num-> num
sum :: [num] -> num
month :: [char] -> bool
reverse :: [*] -> [*]
fst :: (*, **) -> *
snd :: (*, **)-> **
foldr :: (*-> ** -> **) -> ** -> [*] -> **
perms :: [*] -> [[*]]
22 LP TRÌNH HÀM
Các quy tc đnh ngha kiu nh trên làm tránh đc sai sót khi s dng hàm không đúng,
chng hn :
pair 1 true || các tham đi phi cùng kiu
5 + (pair 1 2) || kiu kt qu là mt danh sách, không là mt s.
Trong ví d th hai, Miranda xem rng kiu ca (pair 1 2) là mt danh sách các s,
nên không th dùng đ làm s hng cho mt phép cng.
Dù các hàm có tha nhn các tham đi khác kiu và không cn khai báo kiu mt cách
tng minh, h thng kiu va mô t trên đây là cht ch và dng tnh. S vi phm các quy
tc đnh kiu s dn đn các thông báo li khi dch (compile time error messages).
Tuy nhiên vic thêm các khai báo kiu hàm thng làm cho b kim tra kiu d vi phm
sai sót khi s dng hàm.
I.2.8. Tính hàm theo kiu khôn ngoan
Thông thng, khi gi tính giá tr mt hàm, các tham đi đc tính giá tr trc tiên, sau
đó mi tin hành thc hin tính toán trong hàm. Chng hn, li gi :
mult (fac 3) (fac 4)
yêu cu tính giá tr các hàm giai tha (fac 3) và (fac 4) trc, theo mt th t tùy ý,
sau đó mi thc hin hàm nhân :
mult 6 24
đ cho kt qu cui cùng là 144. Cách vit rút gn biu thc lúc đu thành dng đn gin hn
đc gi là phép rút gn theo th t áp dng (applicative order reduction). Chú ý rng phép
rút gn đc bt đu t các biu thc trong cùng nht (innermost expressions).
Tuy nhiên có mt cách khác đ rút gn mt biu thc là bt đu t biu thc ngoài nht
(outermost expression) và không tính giá tr các biu thc con (subexpressions) cho đn khi
cn dùng đn kt qu ca chúng.
Ngi ta gi cách này là rút gn theo th t thng (normal order reduction). Chng hn
biu thc :
mult (fac 3) (fac 4)
s đc rút gn thành :
(fac 3) * (fac 4)
sau đó thành :
6 * 24
đ nhn đc kt qu 144.
Nhng ngi lp trình có kinh nghim trên các ngôn ng mnh lnh có th ngh rng
phép rút gn theo th t áp dng là thun tin hn, nhng cách này cng có nhng điu bt
tin.
Gi s rng ta mun xây dng hàm cond nhn mt giá tr bool b nh là tham đi th
nht và tr v tham đi th hai ca nó nu b có giá tr true hoc tr v tham đi th ba nu
b là false :
cond b x y = x, if b
cond b x y = y, otherwise
hay gn hn :
NGUYÊN LÝ LP TRÌNH HÀM 23
cond true x y = x
cond false x y = y
Nu c ba tham đi ca cond đc tính giá tr trc khi hàm cond đc thc hin, thì
s xy ra hai trng hp :
− Mt trong các tham đi ca cond tham gia tính toán mt cách vô ích, dn đn kt qu
sai, chng hn cond (x=0) 0 (1/x) vi x=0.
− Nu vic tính toán biu thc tham đi là vô ích và không kt thúc thì s gây ra toàn b
biu thc b tính lp vô hn ln.
Trng hp th hai có th đc minh ha qua ví d sau : gi s ta cng s dng hàm
cond cho mt đnh ngha khác ca hàm fac :
fac n = cond (n = 0) 1 (n * fac (n - 1))
Nu tham đi th ba ca cond luôn luôn đc tính, thì hàm fac s không bao gi dng.
Bi vì li gi fac 1 kéo theo li gi fac 0, li gi fac 0 s kéo theo li gi fac − 1
và c th tip tc.
Nhng vn đ trên đã dn đn khái nim tính giá tr theo kiu khôn ngoan trong lp trình
hàm s dng phép rút gn theo th t thng. Ý tng ca phng pháp tính giá tr hàm theo
kiu khôn ngoan là ch tính giá tr các tham đi ca mt hàm khi các giá tr ca chúng là cn
thit, nhng biu thc nào không cn thit thì b qua.
S dng tính giá tr hàm theo kiu khôn ngoan, phép tính hàm fac 1 s đc rút gn
nh đc trình bày di đây. Vic tính giá tr kt thúc và cho kt qu đúng. đây ta s dng
biu thc if dng gi ng (pseuclo-notation). Tham đi th ba ca cond s không đc
tính nu n bng 0, vì rng trong trng hp này, không cn tính tham đi na.
fac 1
--> cond (1=0) 1 (1*fac (1-1)) || gi fac
--> if (1=0) then 1 else (1*fac (1-1)) || gi cond
--> if false then 1 else (1*fac (1-1)) || tính 1 = 0
--> 1*fac (1-1)
--> 1*(cond (1-1 = 0) 1 ((1-1)-1))) || gi fac
--> 1*((if 1-1=0) then 1 else ((1-1)*fac ((1-1)-1)))
|| gi cond
--> 1*((if true then 1 else ((1-1)*fac ((1-1)-1)))
|| tính 1-1 = 0
--> 1*1 || tính 1*1
--> 1
Phng pháp tính hàm kiu khôn ngoan còn th hin mt li ích quan trng khác : cho
phép đnh ngha các cu trúc d liu vô hn theo kiu ý nim (conceptually infinite). Chng
hn trong Miranda, danh sách các s nguyên dng có th đc đnh ngha :
[1.. ]
Danh sách theo kiu đnh ngha này có th tham gia tính toán nh mi danh sách khác.
Chng hn :
hd [1.. ]
--> 1 || phn t th nht
hd (tl [1.. ])