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

Nguyên lý lập trinh hàm_chương 1

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 (855.63 KB, 64 trang )




i



CHNG I. NGUYÊN LÝ LP 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 lp trình
I.1.1. Vài nét v lch s
Bui ban đu
hng ngôn ng lp trình (programming language) đu tiên trên máy tính đin t là
ngôn ng máy (machine language), t hp ca các con s h hai, hay h nh phân, hay
các bit (vit tt ca binary digit) 0 và 1. Ngôn ng máy ph thuc hoàn toàn vào kin
trúc phn cng ca máy tính và nhng quy c kht khe ca nhà ch to.  gii các bài
toán, ngi lp trình phi s dng mt tp hp các lnh điu khin rt s cp mà mi lnh là
mt t hp các s h hai nên gp rt nhiu khó khn, mt nhc, rt d mc phi sai sót, nhng
li rt khó sa li.
N
T nhng nm 1950, đ gim nh vic lp trình, ngi ta đa vào k thut chng trình
con (sub-program hay sub-routine) và xây dng các th vin chng trình (library) đ khi
cn thì gi đn hoc dùng li nhng đon chng trình đã vit.
Ngôn ng máy tin gn đn ngôn ng t nhiên
Cng t nhng nm 1950, ngôn ng hp dch, hay hp ng (assembly) hay cng còn
đc gi là ngôn ng biu tng (symbolic) ra đi. Trong hp ng, các mã lnh và đa ch
các toán hng đc thay th bi các t ting Anh gi nh (mnemonic) nh ADD, SUB, MUL,
DIV, JUMP... tng ng vi các phép toán s hc + - × /, phép chuyn điu khin, v.v...


Do máy tính ch hiu ngôn ng máy, các chng trình vit bng hp ng không th chy
ngay đc mà phi qua giai đon hp dch (assembler) thành ngôn ng máy. Tuy nhiên, các
hp ng vn còn ph thuc vào phn cng và xa l vi ngôn ng t nhiên (natural language),
ngi lp trình vn còn gp nhiu khó khn khi gii các bài toán trên máy tính.
Nm 1957, hãng IBM đa ra ngôn ng FORTRAN (FORmula TRANslator). ây là ngôn
ng lp trình đu tiên gn gi ngôn ng t nhiên vi cách din đt toán hc. FORTRAN cho
phép gii quyt nhiu loi bài toán khoa hc, k thut và sau đó đc nhanh chóng ng dng
rt rng rãi cho đn ngày nay vi kho tàng th vin thut toán rt đ s và tin dng. Tip
theo là s ra đi ca các ngôn ng ALGOL 60 (ALGOrithmic Language) nm 1960, COBOL
(Comon Business Oriented Language) nm 1964, Simula nm 1964, v.v...
1
2 LP TRÌNH HÀM
Phát trin ca ngôn ng lp trình
Theo s phát trin ca các th h máy tính, các ngôn ng lp trình cng không ngng
đc ci tin và hoàn thin đ càng ngày càng đáp ng nhu cu ca ngi s dng và gim
nh công vic lp trình. Rt nhiu ngôn ng lp trình đã ra đi trên nn tng lý thuyt tính
toán (theory of computation) và hình thành hai loi ngôn ng : ngôn ng bc thp và ngôn
ng bc cao.
Các ngôn ng bc thp (low-level language), hp ng và ngôn ng máy, thng ch dùng
đ vit các chng trình điu khin và kim tra thit b, chng trình sa li (debugger) hay
công c...
Các ngôn ng lp trình bc cao (high-level language) là phng tin giúp ngi làm tin
hc gii quyt các vn đ thc t nhng đng thi cng là ni mà nhng thành tu nghiên
cu mi nht ca khoa hc máy tính đc đa vào. Lnh vc nghiên cu phát trin các ngôn
ng lp trình va có tính truyn thng, va có tính hin đi. Ngày nay, vi nhng tin b ca
khoa hc công ngh, ngi ta đã có th s dng các công c hình thc cho phép gim nh
công vic lp trình t lúc phân tích, thit k cho đn s dng mt ngôn ng lp trình.
I.1.2. nh ngha mt ngôn ng lp trình
Các ngôn ng lp trình bc cao đc xây dng mô phng ngôn ng t nhiên, thng là
ting Anh (hoc ting Nga nhng nm trc đây). nh ngha mt ngôn ng lp trình là đnh

ngha mt vn phm (grammar) đ sinh ra các câu đúng ca ngôn ng đó. Có th hình dung
mt vn phm gm bn thành phn : b ký t, b t vng, cú pháp và ng ngha.
1. B ký t (character set)
Gm mt s hu hn các ký t (hay ký hiu) đc phép dùng trong ngôn ng. Trong các
máy tính cá nhân, ngi ta thng s dng các ký t ASCII. Có th hiu b ký t có vai trò
nh bng ch cái (alphabet) ca mt ngôn ng t nhiên đ to ra các t (word).
2. B t vng (vocabulary)
Gm mt tp hp các t, hay đn v t vng (token), đc xây dng t b ký t. Các t
dùng đ to thành câu lnh trong mt chng trình và đc phân loi tu theo vai trò chc
nng ca chúng trong ngôn ng. Chng hn chng trình Pascal sau đây :
program P;
var ×, y : integer;
begin
read(x);
y:=x+2;
write(y)
end.
gm các đn v t vng :
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.
Hng (constants) : 2
Phép toán (operators) : + , :=
Du phân cách (delimiters) : :, (, ), ...


NGUYÊN LÝ LP TRÌNH HÀM 3
3. Cú pháp (syntax)
Cú pháp quy đnh cách thc kt hp các ký t thành t, kt hp các t thành câu lnh
đúng (statement hay instruction), kt hp các câu lnh đúng thành mt chng trình hoàn
chnh v mt vn phm. Có th hình dung cách kt hp này ging cách đt câu trong mt
ngôn ng t nhiên. Thng ngi ta dùng s đ cú pháp (syntax diagram) hoc dng chun
Backus-Naur (Backus-Naur Form, vit tt BNF), hoc dng chun Backus-Naur m rng
(EBNF − Extended Backus-Naur Form) đ mô t cú pháp ca vn phm.
Ví d I.1.1 : Trong ngôn ng Pascal (hoc trong phn ln các ngôn ng lp trình), tên gi,
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 mt s đ cú pháp, các ô hình ch nht ln lt phi đc thay th bi các ô hình
tròn. Quá trình thay th thc hin th t theo chiu mi tên cho đn khi nhn đc câu đúng.
Chng hn có th «đc» s đ trên nh sau : tên phi bt đu bng ch, tip theo có th là
ch hoc s tu ý, ch ch có th là mt trong các ch cái A..Za..z, s ch có th là mt trong
các ch s 0..9. Nh vy, Delta, x1, x2, Read, v.v... là các tên vit đúng, còn 1A, β, π,
bán kính, v.v... đu không phi là tên vì vi phm quy tc cú pháp.
Vn phm BNF gm mt dãy quy tcc. Mi quy tc gm v trái, du đnh ngha ::= (đc
đc đnh ngha bi) và v phi. V trái là mt ký hiu phi đc đnh ngha, còn v phi là
mt dãy các ký hiu, hoc đc tha nhn, hoc đã đc đnh ngha t trc đó, tuân theo
mt quy c nào đó. EBNF dùng các ký t quy c nh sau :

Ký hiu Ý ngha
::=, hoc →, hoc = đc đnh ngha là
{ } chui ca 0 hay nhiu mc lit kê tu chn (option)
[] hoc 0 hoc 1 mc lit kê tu chn
< > mc lit kê phi đc thay th
| hoc (theo ngha loi tr)
Các quy tc BNF đnh ngha 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
Vn phm ca mt ngôn ng lp trình đn gin dng EBNF nh sau :
<program> ::= program <statement>
*
end
<statement> ::= <assignment> | <loop>
<assignment> ::= <identifier> := <expression> ;
4 LP 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’
Mt câu, tc là mt chng trình đn gin, vit trong vn phm trên nh sau :
program

n := 1 ;
while n <= 10 do n := n + 1 ; done
end
4. Ng ngha (semantic)
Cn c vào cú pháp ca ngôn ng lp trình, ngi lp trình vit chng trình gm các câu
lnh theo trình t cho phép đ gii quyt đc bài toán ca mình.  đt đc mc đích đó,
mi câu lnh vit ra không nhng đúng đn v mt cú pháp, mà còn phi đúng đn c v mt
ng ngha, hay ý ngha logic ca câu lnh. Tính đúng đn v mt ng ngha cho phép gii
quyt đc bài toán, chng trình chy luôn luôn dng, n đnh và cho kt qu phù hp vi
yêu cu đt ra ban đu.
I.1.3. Khái nim v chng trình dch
Chng trình đc vit trong mt ngôn ng lp trình bc cao, hoc bng hp ng, đu
đc gi là chng trình ngun (source program).
Bn thân máy tính không hiu đc các câu lnh trong mt chng trình ngun. Chng
trình ngun phi đc dch (translate) thành mt chng trình đích (target program) trong
ngôn ng máy (là các dãy s 0 và 1), máy mi có th đc «hiu» và thc hin đc. Chng
trình đích còn đc gi là chng trình thc hin (executable program).
Chng trình trung gian đm nhim vic dch đó đc gi là các chng trình dch.
Vic thit k chng trình dch cho mt ngôn ng lp trình đã cho là cc k khó khn và
phc tp. Chng trình dch v nguyên tc phi vit trên ngôn ng máy đ gii quyt vn đ
x lý ngôn ng và tính vn nng ca các chng trình ngun. Tuy nhiên, ngi ta thng s
dng hp ng đ vit các chng trình dch. Bi vì vic dch mt chng trình hp ng ra
ngôn ng máy đn gin hn nhiu. Hin nay, ngi ta cng vit các chng trình dch bng
chính các ngôn ng bc cao hoc các công c chuyên dng.
Thông thng có hai loi chng trình dch, hay hai ch đ dch, là trình biên dch và
trình thông dch, hot đng nh sau :
Trình biên dch (compilater) dch toàn b chng trình ngun thành chng trình đích
ri sau đó mi bt đu tin hành thc hin chng trình đích.

• Trình thông dch (interpreter) dch ln lt tng câu lnh mt ca chng trình ngun

ri tin hành thc hin luôn câu lnh đã dch đó, cho ti khi thc hin xong toàn b
chng trình.
Có th hiu trình biên dch là dch gi, trình thông dch là thông dch viên.

NGUYÊN LÝ LP TRÌNH HÀM 5
Nhng ngôn ng lp trình cp cao  ch đ biên dch hay gp là : Fortran, Cobol, C,
C++, Pascal, Ada, Basic...  ch đ thông dch hay ch đ tng tác : Basic,Lisp, Prolog...
I.1.4. Phân loi các ngôn ng lp trình
Cho đn nay, đã có hàng trm ngôn ng lp trình đc đ xut nhng trên thc t, ch có
mt s ít ngôn ng đc s dng rng rãi. Ngoài cách phân loi theo bc nh đã nói  trên,
ngi ta còn phân loi ngôn ng lp trình theo phng thc (paradigm), theo mc đ quan
trng (measure of emphasis), theo th h (generation), v.v...
Cách phân loi theo bc hay mc (level) là da trên mc đ tru tng so vi các yu t
phn cng, chng hn nh lnh (instructions) và cp phát b nh (memory allocation).
Mc Lnh S dng b nh Ví d
Thp Lnh máy đn gin Truy cp và cp phát trc tip Hp ng, Autocode
Cao
Biu thc và điu khin
tng minh
Truy cp và cp phát nh các phép
toán, chng hn new
FORTRAN, ALGOL,
Pascal, C, Ada
Rt cao Máy tru tng Truy cp n và t đng cp phát
SELT, Prolog,
Miranda
Hình I.2. Ba mc ca ngôn ng lp trình.
Nhng nm gn đây, ngôn ng lp trình đc phát trin theo phng thc lp trình (còn đc gi
là phong cách hay kiu lp trình). Mt phng thc lp trình có th đc hiu là mt tp hp các tính
nng tru tng (abstract features) đc trng cho mt lp ngôn ng mà có nhiu ngi lp trình

thng xuyên s dng chúng. S đ sau đây minh ho s phân cp ca các phng thc lp trình :
Khai báo
Phng thc lp trình
Mnh lnh
C s
d liu
Hàm
Lôgic
X lý
song song
Hng
đi tng
Th tc

Hình I.3. Phân cp ca các phng thc lp trình.
Sau đây là mt s ngôn ng lp trình quen thuc lit kê theo phng thc :
Các ngôn ng mnh lnh (imperative) có Fortran (1957), Cobol (1959), Basic (1965),
Pascal (1970), C (1971), Ada (1979)...






Các ngôn ng đnh hng đi tng (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 da logic (logic-based) ch yu là ngôn ng Prolog (1970).
Ngôn ng thao tác c s d liu 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ó mt s phng thc lp trình đang đc phát trin ng dng nh :
6 LP TRÌNH HÀM
Lp trình phân b (distributed programming). •



Lp trình ràng buc (constraint programming).
Lp trình hng truy cp (access-oriented programming).
Lp trình theo lung d liu (dataflow programming), v.v...
Vic phân loi các ngôn ng lp trình theo mc đ quan trng là da trên cái gì (what) s
thao tác đc (achieved), hay tính đc (computed), so vi cách thao tác nh th nào (how).
Mt ngôn ng th hin cái gì s thao tác đc mà không ch ra cách thao tác nh th nào
đc gi là ngôn ng đnh ngha (definitional) hay khai báo (declarative). Mt ngôn ng th
hin cách thao tác nh th nào mà không ch ra cái gì s thao tác đc gi là ngôn ng thao
tác (operational) hay không khai báo (non-declarative), đó là các ngôn ng mnh lnh.

Hình I.4. Phát trin ca ngôn ng lp trình.
Các ngôn ng lp trình cng đc phân loi theo th h nh sau :
Th h 1 : ngôn ng máy •





Th h 2 : hp ng

Th h 3 : ngôn ng th tc
Th h 4 : ngôn ng áp dng hay hàm
Th h 5 : ngôn ng suy din hay da logic
Th h 6 : mng n-ron (neural networks)

NGUYÊN LÝ LP TRÌNH HÀM 7
Trc khi nghiên cu lp các ngôn ng lp trình hàm, ta cn nhc li mt s đc đim
ca lp các ngôn ng lp trình mnh lnh.
I.1.5. Ngôn ng lp trình mnh lnh
Trong các ngôn ng mnh lnh, ngi lp trình phi tìm cách din đt đc thut toán,
cho bit làm cách nào đ gii mt bài toán đã cho. Mô hình tính toán s dng mt tp hp
(hu hn) các trng thái và s thay đi trng thái. Mi trng thái phn ánh ni dung các bin
d liu đã đc khai báo. Trng thái luôn b thay đi do các lnh điu khin và các lnh gán
giá tr cho các bin trong chng trình. Chng trình biên dch cho phép lu gi các trng
thái trong b nh chính và thanh ghi, ri chuyn các phép toán thay đi trng thái thành các
lnh máy đ thc hin.

Kiu :
tp hp giá tr
tp hp phép toán
cu trúc lu tr
bit
du
2
14
2
13
2
2


. . .
2
1

2
0

biu din theo bit :
0
0 0
1 . . .
0 1
Tên bin
i
Tên kiu
integer
..., −1, 0, 1, ...
+, −, ×, /, ...
Mc
ngôn ng
Mc
chng
trình
dch
S
5
Mc
máy
Hình I.5. Quan h gia tên bin, kiu và giá tr trong ngôn ng mnh lnh
Hình I.5. minh ha cách khai báo d liu trong các ngôn ng mnh lnh và các mi quan

h theo mc. Ngi ta phân bit ba mc nh sau : mc ngôn ng liên quan đn tên bin, tên
kiu d liu và cu trúc lu tr ; mc chng trình dch liên quan đn phng pháp t chc
b nh và mc máy cho bit cách biu din theo bit và giá tr d liu tng ng. Mi khai
báo bin, ví d int i, ni kt (bind) tên bin (i) vi mt cu trúc đc trng bi tên kiu
(int) và vi mt giá tr d liu đc biu din theo bit nh lnh gán i := 5 (hoc nh mt
lnh va khai báo va khi gán int i=5). T hp tên, kiu và giá tr đã to nên đc trng
ca bin.
Các ngôn ng mnh lnh đc s dng hiu qu trong lp trình do ngi lp trình có th
tác đng trc tip vào phn cng. Tuy nhiên, tính thc dng mnh lnh làm hn ch trí tu
ca ngi lp trình do phi ph thuc vào cu trúc vt lý ca máy tính. Ngi lp trình luôn
có khuynh hng suy ngh v nhng v trí lu tr d liu đã đc đt tên (nguyên tc đa ch
hoá) mà ni dung ca chúng thng xuyên b thay đi. Thc t có rt nhiu bài toán cn s
tru tng hoá khi gii quyt (ngha là không ph thuc vào cu trúc vt lý ca máy tính),
không nhng đòi hi tính thành tho ca ngi lp trình, mà còn đòi hi kin thc Toán hc
tt và kh nng tru tng hoá ca h.
T nhng lý do trên mà ngi ta tìm cách phát trin nhng mô hình tng tác không phn
ánh mi quan h vi phn cng ca máy tính, mà làm d dàng lp trình. Ý tng ca mô hình
là ngi lp trình cn đc t cái gì s đc tính toán mà không phi mô t cách tính nh th
nào. S khác nhau gia «nh th nào» và «cái gì», cng nh s khác nhau gia các ngôn ng
8 LP TRÌNH HÀM
mnh lnh và các ngôn ng khai báo, không phi luôn luôn rõ ràng. Các ngôn ng khai báo
thng khó cài đt và khó vn hành hn các ngôn ng mnh lnh. Các ngôn ng mnh lnh
thng gn gi ngi lp trình hn.
Sau đây là mt s đc trng ca ngôn ng lp trình mnh lnh :
− S dng nguyên lý tinh ch tng bc hay làm mn dn, x lý ln lt các đi tng
d liu đã đc đt tên.
− Khai báo d liu đ ni kt mt tên bin đã đc khai báo vi mt kiu d liu và mt
giá tr. Phm vi hot đng (scope) ca các bin trong chng trình đc xác đnh bi
các khai báo, hoc toàn cc (global), hoc cc b (local).
− Các kiu d liu c bn thông dng là s nguyên, s thc, ký t và lôgic. Các kiu mi

đc xây dng nh các kiu cu trúc. Ví d kiu mng, kiu bn ghi, kiu tp hp,
kiu lit kê,...
− Hai kiu d liu có cùng tên thì tng đng vi nhau, hai cu trúc d liu là tng
đng nu có cùng giá tr và có cùng phép toán x lý.
− Trng thái trong (b nh và thanh ghi) b thay đi bi các lnh gán. Trng thái ngoài
(thit b ngoi vi) b thay đi bi các lnh vào-ra. Giá tr đc tính t các biu thc.
− Các cu trúc điu khin là tun t, chn la (r nhánh), lp và gi chng trình con.
− Chng trình con thng có hai dng : dng th tc (procedure) và dng hàm
(function). S khác nhau ch yu là hàm luôn tr v mt giá tr, còn th tc thì không
không nht thit tr v giá tr. Vic trao đi tham bin (parameter passing) vi chng
trình con hoc theo tr (by value) và theo tham chiu (by reference).
− S dng chng trình con thng gây ra hiu ng ph (side effect) do có th làm thay
đi bin toàn cc.
− Mt chng trình đc xây dng theo bn mc : khi (block), chng trinh con, đn
th (module/packages) và chng trình.
I.2 C s ca các ngôn ng hàm
I.2.1. Tính khai báo ca các ngôn ng hàm
Trong các ngôn ng mnh lnh, mt chng trình thng cha ba li gi chng trình
con (th tc, hàm) liên quan đn quá trình đa vào d liu, x lý d liu và đa ra kt qu
tính toán nh sau :
begin
GetData(...) ; { đa vào }
ProcessData(...); { x lý }
OutPutResults(...); { xem kt qu }
end
Trong các ngôn ng lp trình hàm, các li gi chng trình con đc vit thành biu thc
rt đn gin :
(print
(process-data
(get-data (...))))


NGUYÊN LÝ LP TRÌNH HÀM 9
Các ngôn ng hàm là cng các ngôn ng bc cao, mang tính tru tng hn so vi các
ngôn ng mnh lnh.
Nhng ngi lp trình hàm thng tránh s dng các bin toàn cc, trong khi đó, hu ht
nhng ngi lp trình mnh lnh đu phi s dng đn bin toàn cc.
Khi lp trình vi các ngôn ng hàm, ngi lp trình phi đnh ngha các hàm toán hc d
suy lun, d hiu mà không cn quan tâm chúng đc cài đt nh th nào trong máy.
Nhng ngi theo khuynh hng lp trình hàm cho rng các lnh trong mt chng trình
vit bng ngôn ng mnh lnh làm thay đi trng thái toàn cc là hoàn toàn bt li. Bi vì rt
nhiu phn khác nhau ca chng trình (chng hn các hàm, các th tc) tác đng không trc
tip lên các bin và do vy làm chng trình khó hiu. Các th tc thng đc gi s dng
 các phn khác nhau ca chng trình gi nên rt khó xác đnh các bin b thay đi nh th
nào sau li gi. Nh vy, s xut hin hiu ng ph làm cn tr vic chng minh tính đúng
đn (correctness proof), cn tr ti u hóa (optimization), và cn tr quá trình song song t
đng (automatic parrallelization) ca chng trình.
Mt ngôn ng hàm, hay ngôn ng áp dng (applicative language) da trên vic tính giá
tr ca biu thc đc xây dng t bên ngoài li gi hàm.  đây, hàm là mt hàm toán hc
thun túy : là mt ánh x nhn các giá tr ly t mt min xác đnh (domain) đ tr v các giá
tr thuc mt min khác (range hay co-domain).
Mt hàm có th có, hoc không có, các tham đi (arguments hay parameters) đ sau khi
tính toán, hàm tr v mt giá tr nào đó. Chng hn có th xem biu thc 2 + 3 là hàm tính
tng (phép +) ca hai tham đi là 2 và 3.
Ta thy rng các hàm không gây ra hiu ng ph trong trng thái ca chng trình, nu
trng thái này đc duy trì cho các tham đi ca hàm. Tính cht này đóng vai trò rt quan
trng trong lp trình hàm. ó là kt qu ca mt hàm không ph vào thi đim (when) hàm
đc gi, mà ch ph thuc vào cách gi nó nh th nào đi vi các tham đi.
Trong ngôn ng lp trình mnh lnh, kt qu ca biu thc :
f(x) + f(x)
có th khác vi kt qu :

2 * f(x)
vì li gi f(x) đu tiên có th làm thay đi x hoc mt bin nào đó đc tip cn bi f. Trong
ngôn ng lp trình hàm, c hai biu thc trên luôn có cùng giá tr.
Do các hàm không ph thuc nhiu vào các bin toàn cc, nên vic lp trình hàm s d
hiu hn lp trình mnh lnh. Ví d gi s mt trình biên dch cn ti u phép tính :
f(x) + f(x)
thành :
2 * f(x)
Khi đó, trình biên dch mt ngôn ng hàm luôn luôn xem hai kt qu là mt, do có tính
nht quán trong kt qu tr v ca hàm. Tuy nhiên, mt trình biên dch ngôn ng mnh lnh,
ngôn ng Ada
1
chng hn, thì đu tiên phi chng minh rng kt qu ca li gi th hai
không ph thuc vào các bin đã b thay đi trong quá trình thc hin bi li gi th nht.


1
Ada là ngôn ng lp trình bc cao đc phát trin nm 1983 bi B Quc phòng M (US Department of
Defense), còn gi là Ada 83, sau đó đc phát trin bi Barnes nm 1994, gi là Ada 9X. Ngôn ng Ada ly tên
ca nhà n Toán hc ngi Anh, Ada Augusta Lovelace, con gái ca nhà th Lord Byron (1788−1824). Ngi
ta tôn vinh bà là ngi lp trình đu tiên.
10 LP TRÌNH HÀM
Mt trình biên dch song song s gp phi vn đ tng t nu trình này mun gi hàm
theo kiu gi song song.
Bên cnh tính u vit, ta cng cn xem xét nhng bt li vn có ca lp trình hàm :
nhc đim ca ngôn ng hàm là thiu các lnh gán và các bin toàn cc, s khó khn trong
vic mô t các cu trúc d liu và khó thc hin quá trình vào/ra d liu.
Tuy nhiên, ta thy rng s thiu các lnh gán và các bin toàn cc không nh hng hay
không làm khó khn nhiu cho vic lp trình. Khi cn, lnh gán giá tr cho các bin đc mô
phng bng cách s dng c cu tham bin ca các hàm, ngay c trong các chng trình vit

bng ngôn ng mnh lnh.
Chng hn ta xét mt hàm P s dng mt bin cc b x và tr v mt giá tr có kiu bt
k nào đó (SomeType). Trong ngôn ng mnh lnh, hàm P có th làm thay đi x bi gán
cho x môt giá tr mi. Trong mt ngôn ng hàm, P có th mô phng s thay đi này bi
truyn giá tr mi ca x nh là mt tham đi cho mt hàm ph tr thc hin phn mã còn li
ca P. Chng hn, s thay đi giá tr ca bin trong chng 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 vit li nh sau :
function P(n : integer) −> SomeType ;
x: integer := n + 7
begin
return Q(3*x + 1) % mô phng x := x * 3 + 1
end ;
trong đó, hàm mi Q đc đnh ngha nh sau :
function Q(x: integer) −> Some Type
begin
return 5 * g(x)
end ;
Ta cng có th s dng k thut này cho các bin toàn cc. Nh vy, vic mô phng lp
trình mnh lnh trong mt ngôn ng hàm không phi là cách mong mun, nhng có th làm
đc.
Mt vn đ ni bt trong ngôn ng hàm là s thay đi mt cu trúc d liu. Trong ngôn
ng mnh lnh, s thay đi mt phn t ca mt mng rt đn gin. Trong ngôn ng hàm,
mt mng không th b thay đi. Ngi ta phi sao chép mng, tr ra phn t s b thay đi,
và thay th giá tr mi cho phn t này. Cách tip cn này kém hiu qu hn so vi phép gán

cho phn t.
Mt vn đ khác ca lp trình hàm là kh nng hn ch trong giao tip gia h thng
tng tác vi h điu hành hoc vi ngi s dng. Tuy nhiên hin nay, ngi ta có xu
hng tng cng th vin các hàm mu x lý hng đi tng trên các giao din đ ho
(GUI-Graphic User Interface). Chng hn các phiên bn thông dch h Lisp nh DrScheme,
MITScheme, WinScheme...

NGUYÊN LÝ LP TRÌNH HÀM 11
Tóm li, ngôn ng hàm da trên vic tính giá tr ca biu thc. Các bin toàn cc và phép
gán b loi b, giá tr đc tính bi mt hàm ch ph thuc vào các tham đi. Thông tin trng
thái đc đa ra tng minh, nh các tham đi ca hàm và kt qu.
Sau đây ta s xét nhng khái nim đc coi là c bn nht trong các ngôn ng hàm : hàm
(function), danh sách (lists), kiu (type), tính đa kiu (polymorphism), các hàm bc cao
(higher−order functions), tham đi hóa tng phn (Currying), tính hàm theo kiu khôn
ngoan
2
(lazy evaluation), phng trình (equations), so khp (patterm matching).
Các hàm đ quy (recursive functions) là mt trong nhng khái nim chính trong ngôn ng
hàm. Các hàm bc cao và phng pháp tính hàm theo kiu khôn ngoan to th mnh cho lp
trình hàm và đóng vai trò quan trng trong vic xây dng các chng trình hàm dng đn th
(modular functional programs). Tính đa kiu b sung tính mm do cho h thng đnh kiu.
Trc khi tìm hiu ngôn ng Scheme bt đu t chng 2, cun sách s dng ngôn ng
Miranda đ trình bày nhng khái nim c bn ca lp trình hàm. Miranda là mt ngôn ng
hàm có cú pháp d đc, d hiu do David Turner phát trin nm 1986. c đim ca Miranda
là thun tuý hàm (purely functional), không xy ra hiu ng ph. Mt chng trình Miranda,
đc gi là mt «script», là mt tp hp các phng trình (equation) đc đnh ngha theo
mt th t tu ý nào đó (nói chung th t không quan trng).
Trình thông dch Miranda chy ch đ tng tác trong h điu hành Unix. Sau đây, chúng
tôi không trình bày đy đ cú pháp ca Miranda mà ch qua các ví d ca Miranda đ minh
ho các yu t thun tuý hàm ca lp trình hàm.

Ví d sau đây la mt chng trình Miranda đn gin. Chú ý cp ký hiu || đ bt đu
mt dòng chú thích ca 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 ngha hàm
Hàm là khái nim c bn trong các ngôn ng hàm. Mt hàm có th nhn t không đn
nhiu tham đi vào đ tính toán và tr v mt giá tr, giá tr này ch ph thuc vào các tham
đi đã nhn mà thôi.
Trong Miranda, mt hàm đc đnh ngha bi hai phn : phn khai báo và phn đnh
ngha hàm. Phn khai báo có th vng mt có dng mt nguyên mu hàm :
<tên hàm> :: <min xác đnh> -> <min giá tr>
Phn đnh ngha hàm có dng mt phng trình, gm v trái và mt s v phi, mi v
phi có th có mt điu kin đóng vai trò «lính gác» (guard) phân bit đng cui :
<tên hàm> [ <danh sách tham đi> ] = <biu thc> [ <điu kin> ]
Ví d hàm đi nhit đ t đ Fahrenheit (F) sang đ Celsius (C) đc đnh ngha trong
Miranda nh sau :
celsius :: num −> num || khai báo kiu hàm
celsius f = (f − 32)*5/9 || đi t đ Fahrenheit sang đ Celsius


2
Trong cun sách có nhiu thut ng ting Vit do tác gi t dch t ting Anh.

12 LP TRÌNH HÀM
Dòng đu tiên khai báo celsius là mt hàm nhn mt đi s và tr v mt giá tr s.
Dòng th hai tính đi nhit đ. Áp dng hàm celsius đã đnh ngha cho các đi s c th,
ta có :
celsius 68
--> 20
celsius (-40)
--> -40
 đây, ký hiu --> dùng đ ch rng giá tr tr v ca hàm là nhng gì theo sau ký hiu
này. Chng hn, li gi celsius 68 tr v giá tr 20.
Ngôn ng Miranda s dng cú pháp ngn gn và không s dng các cp du ngoc đ
bao bc các tham đi hoc bao bc các li gi hàm.
Các hàm đ quy đóng vai trò quan trng trong các ngôn ng hàm. Chng hn, xét mt ví
d c đin là tính c s chung ln nht gcd (greatest common divisor) theo thut toán
Euclide :
gcd a b = gcd (a-b) b, if a > b
= gcd a (b-a), if a < b
= a, if a=b || hoc otherwise
Hàm gcd đc đnh ngha nh phng trình có 3 v phi phân bit, mi v phi cha
mt điu kin (a > b, a < b và a=b hay otherwise). Miranda s thc hin gi đ quy
cho đn khi gp điu kin dng là a=b và cng là kt qu tính gcd. Ta thy các lính gác
trong phng trình thc hin tng t lnh if trong ngôn ng mnh lnh.
Trong các giáo trình nhp môn Tin hc, ngi ta thng ly ví d tính giai tha ca mt
s nguyên không âm bng phng pháp đ quy. Ta s thy ngôn ng Miranda tính giai tha
kiu đ quy nh sau :
fac :: num −> num
fac n = 1, if n = 0 || fac 0 = 1
fac x = x * fac (x - 1), otherwise || li gi đ quy
 so sánh, chng trình di đây ch ra cách tính n! trong mt ngôn ng mnh lnh
nhng không dùng đ quy (ch dùng đ quy khi tht cn thit !).

function fac (n: integer) : integer ;
var i, r: integer ; % r cha kt 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 dng mt bin trng thái r b thay đi giá tr trong vòng lp for.
Còn chng trình Miranda không s dng vòng lp for, mà s dng mt li gi đ quy. Mi
li gi đ quy cho fac tng ng vi mt ln gp ca vòng lp for. Do tính ngn gn ca
li gi đ quy trong ngôn ng hàm, nhiu ngi ng h lp trình hàm quan nim rng phép
lp trong các ngôn ng mnh lnh là rc ri, làm ngi đc khó hiu, khó theo dõi.
Sau đây là mt ví d khác tính nghim mt phng trình bc hai ax
2
+bx+c=0 :

NGUYÊN LÝ LP 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
Mnh đ ca Miranda có th lng nhau (nested) nhiu mc.
I.2.3. Danh sách
Trong hu ht các ngôn ng hàm, danh sách (list) là cu trúc d liu quan trng nht.
Trong ngôn ng Miranda, mt danh sách gm t 0 đn nhiu phn t có cùng kiu, ví d :
[1, 4, 9, 16] || danh sách gm 4 s nguyên
[1..10] || danh sách 10 s nguyên (1..10)

[’u’, ’f’, ’0’] || danh sách gm 3 ký t
[] || danh sách rng (empty list) không có phn t nào
[[2, 4, 6], [0, 3, 6, 9]] || danh sách cha hai danh sách các s
nguyên
week_days = ["Mon","Tue","Wed","Thur","Fri"]
Các danh sách có th lng nhau (bao hàm nhau). Mt đim mnh ca Miranda và ca mt
s ngôn ng hàm khác là cho phép đnh ngha mt danh sách nh tính cht d nhn bit ca
các phn t ca nó, tng t cách xác đnh mt tp hp trong Toán hc. Mt danh sách đc
đnh ngha nh vy đc gi là nhn bit đc hay ngm hiu (list comprehension). Ví d,
cách vit :
[ n | n <- [1..10] : n mod 2 = 1]
--> [1, 3, 5, 7, 9]
ch đnh mt danh sách có th t ca các s l gia 1 và 10. Danh sách đc hiu là «gm tt
c các s n nm gia 1 và 10 tho mãn n mod 2 bng 1». Mt ví d khác v danh sách nhn
bit đc :
[ n*n | n <- [1..100]] || danh sách bình phng các s gia 1 và 100
Dng tng quát ca danh sách nhn bit đc :
[ body | qualifiers ]
trong đó mi qualifier hoc là mt b to sinh phn t (generator) có dng nh sau :
var <- exp
ký hiu <− biu din phn t var thuc (∈) mt tp hp ch đnh bi exp, hoc là mt b
lc có dng là mt biu thc lôgic thu hp min xác đnh ca các bin đc to ra bi b to
sinh. Khi có nhiu thành phn qualifier cùng xut hin thì chúng đc lit kê cách nhau
mt du : (du hai chm).
Ví d sau đây tr v danh sách các hoán v ca mt danh sách đã cho :
perms [] = [[]]
perms L = [ a:T | a <- L : T <- perms (L--[a]) ]
Ta có th đnh ngha li hàm tính giai tha nh hàm nhân product trong th vin ca
Miranda :
14 LP TRÌNH HÀM

fac n = product [1..n]
Tng t, ta đnh ngha hàm tính tng các s l gia 1 và 100 nh hàm sum trong th
vin ca Miranda :
result = sum [1, 3..100]
Ngi ta xây dng nhiu phép toán khác nhau trên các danh sách. Tùy theo ngôn ng
hàm, mt s phép toán có th có sn (build-in), mt s phép toán khác do ngi s dng t
đnh ngha. Các phép toán s cp nht là xây dng (construction) và phân tách
(decomposing) các danh sách. Chng hn phép toán hd (head) ly ra phn t đu tiên ca
mt danh sách đ tr v mt phn t. Phép toán tl (tail) tr v danh sách còn li (sau khi đã
ly đi phn t đu tiên).
hd [10, 20, 30]
--> 10
tl [10, 20, 30]
--> [20, 30]
Phép toán : (cons) đ chèn mt phn t vào trc các phn t trong mt 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 # (du # đt trc danh sách) đm s phn t là đ dài
(length) mt danh sách, phép ghép (concatenate) ++ (hai du cng liên tip) các danh sách,
phép ! ly t danh sách ra mt s phn t (infix), hàm nghch đo reverse mt 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 mt danh sách
#days
--> 7
days!0
--> "Mon"
Miranda còn có phép -- (hai du tr liên tip) tính hiu ca hai tp hp là hai danh sách :
[1,2,3,4,5] -- [2,4]
--> [1,3,5]
 hiu rõ hn các lp trình trong Miranda, sau đây ta s vit li mt s hàm th vin ca
Miranda. Gi s ta vit hàm length tính đ dài nh sau :

NGUYÊN LÝ LP TRÌNH HÀM 15
length L = 0, if L = [] || danh sách rng có đ dài 0
|| danh sách khác rng có ít nht mt phn t
length L = 1 + length (tl L), otherwise
Tng t, hàm concatenate ghép hai danh sách đc xây dng nh sau :
concatenate L1 L2 = L2, if L1 = []
concatenate L1 L2
= (hd L1):concatenate (tl L1) L2, otherwise
Hàm concatenate đc trng cho kiu lp trình đ quy ca các ngôn ng hàm. Dòng
đu tiên ch cách thoát ra khi hàm, theo kiu chia đ tr (divide and conquer). Trong trng
hp ghép mt danh sách rng (if L1 = []) vi mt danh sách bt k khác (L2) thì tr v
kt qu chính là danh sách này và quá trình kt thúc.
Dòng th hai là trng hp tng quát v ghép hai danh sách thành mt danh sách mi s
dng phép toán cons (otherwise) theo kiu đ quy. Do danh sách th nht L1 ≠ [] nên
L1 có ít nht mt phn t. Khi đó danh sách mi (là danh sách kt qu) đc to ra bi phn
t đu tiên ca L1, là (hd L1) ghép vi (phép :) mt danh sách còn li. Danh sách còn li
này li là kt qu ca bài toán ban đu nhng nh hn vi tham đi th nht là danh sách còn
li ca L1 và tham đi th hai là toàn b L2. Quá trình c th tip tc cho đn khi danh sách

còn li rng.
Ta có th so sánh cách xây dng các danh sách trong các ngôn ng mnh lnh vi ngôn
ng Miranda.
Trong các ngôn ng mnh lnh, danh sách do ngi lp trình t xây dng thng theo
kiu móc ni s dng bin con tr (pointer) và s dng b nh cp phát đng (dynamic
allocation). Các phép toán trên danh sách là s thao tác  mc thp trên các con tr. Vic cp
phát và gii ta b nh cng do ngi s dng t gii quyt. Thông thng, ngi lp trình
có xu hng s dng vic cp nht phá hy (destructive updates) trên các cu trúc d liu.
Ví d, ngi lp trình Ada ghép danh sách bng cách vit mt th tc cho phép móc ni
(hook) hai danh sách vi nhau nh mt phép gán con tr, mà không cn xây dng mt danh
sách mi (th ba). Tuy nhiên tip cn kiu Ada có th gây ra hiu ng ph nu làm thay đi
danh sách th nht.
Trong các ngôn ng hàm, ngi lp trình không h quan tâm đn các con tr và vic cp
phát b nh. Mc du nhng yu t này là tn ti trong ngôn ng hin hành, nhng chúng
đc che du mt cách hiu qu đi vi ngi lp trình to nên mt mc tru tng có li
cho ngi lp trình, giúp h tránh đc nhng sai sót khi lp trình.
Trong nhng tình hung nh vy, mt h thng thi gian thc hin RST (Run Time
System) qun lý b nh lu tr các phn t ca danh sách. RTS cho phép cp phát b nh khi
cn thit và t đng phát hin nhng khi nh không còn cn dùng đn na đ gii phóng
chúng.
Ngôn ng Miranda còn có kiu d liu tuple tng t kiu bn ghi ca Pascal. ó là
mt dãy phn t không có cùng kiu (danh sách là mt dãy phn t có cùng kiu tng t
kiu mng array) đc vit gia mt cp du ngoc. Ví d :
employee = ("Kim",True, False , 29)
Các phn t kiu tuple không có th t. Vic truy cp đn mt phn t ch có th đc
thc hin nh phép so khp (pattern matching).
16 LP TRÌNH HÀM
I.2.4. Phép so khp
Nh đã thy, mt hàm trong Miranda có th đc đnh ngha bng cách s dng nhiu
biu thc v phi khác nhau, theo sau là «lính gác», đó là các điu kin nh if x>0,

otherwise hay while. Tuy nhiên, Miranda cho phép s dng mt cú pháp tng quát đ
la chn gia các kh nng da trên các mu (patterns) dng :
<patterm> = <expression>, <condition>
trong đó, phn <condition> là tùy chn. Mu có th s dng các tham bin hình thc, các
hng và mt s cu trúc khác. Khi mt hàm đc gi, li gi hàm gm tên hàm và các tham
đi thc s ca nó (nu có) s đc so khp vi các mu trong chng trình. Nu so khp
thành công, dòng lnh tng ng vi mu đc thc hin. Nu có nhiu mu đc tìm thy,
mu đu tiên (theo th t vit các lnh) s đc chn.
Sau đây là mt s ví d đn gin minh ha cách đnh ngha các hàm s dng phép so
khp. Hàm cond đã xét có th vit di dng :
cond true x y = x
cond false x y = y
Hàm fac có th vit vi hai dòng chng trình :
fac 0 = 1
fac (n+1) = (n+1) * fac n
Phép so khp th nht xy ra nu tham đi bng 0. Mu th hai đc so ch khi nu tham
đi là ln hn hoc bng 1. Khi đó giá tr ca n đc ly giá tr ca tham đi tr đi 1 mt
cách «khôn ngoan».
Trng hp tng quát, mt biu thc s xut hin trong mt mu có th có dng V + C,
vi V là mt bin và C là mt trc hng (literal constant). Mu s ch đc so nu V có th
nhn mt giá tr không âm. Hàm ackerman đc đnh ngha 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 biu thc s tng quát không th đc ly làm các mu đ tránh nhp nhng
(ambiguities). Ví d, mu :

f (n + m) = n * m
là nhp nhng và không đúng. Mt li gi hàm, f 9 chng hn, s gây ra kt qu hoc 1*8
hoc 2*7, hoc mt t hp khác tu ý.
Mu cng có th cha danh sách. Ví d, hàm tính đ dài ca mt danh sách đã cho trc
đây có th đc vit li di dng nh sau :
length [] = 0
length (a : L) = 1 + (length L)
Mu xut hin trong dòng th nht tng ng vi danh sách rng, ch có danh sách rng
mi th đc so khp đ có kt qu 0. Dòng th hai x lý danh sách khác rng. Trong trng
hp này, mt li gi đ quy đc to ra. Bin a đc khp vi phn t đu tiên ca danh

NGUYÊN LÝ LP TRÌNH HÀM 17
sách tham đi và do đó, L là danh sách còn li tr thành tham đi mi ca hàm. Kt qu li
gi đ quy đã gim đi 1.
Mt s hàm x lý danh sách khác s dng mu so khp : tính tng, tích và nghch đo
mt 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 cp đn mt phn t ca kiu bn ghi (tuple), s dng so khp, chng hn bn ghi
có hai phn t :
fst (a, b) = a || truy cp đn mt phn t th nht
snd (a, b) = b || truy cp đn mt phn t th hai
Sau đây đnh ngha li hai hàm th vin ca Miranda : hàm take tr v danh sách n
phn t đu tiên ca danh sách đã cho và hàm drop tr v phn còn li ca danh sách sau
khi đã loi b n phn 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ú ý rng hai hàm đc đnh ngha sao cho đng nht thc sau đây tho mãn (bao gm
c trng hp oái om là đ dài ca L nh thua n) :
take n L ++ drop n L = L
I.2.5. Phng pháp currying (tham đi hoá tng phn)
Mt yu t quan trng khác ca hu ht các ngôn ng hàm là phng pháp currying (ly
tên nhà logic hc Haskell B. CURRY.), hay còn đc gi là phng pháp tham đi hoá tng
phn (partial parametrization).
Thông thng mt hàm đc thc hin theo kt hp trái, nu vit f x y (hàm f tác đng
lên hai đi x y), vit quy c (x, y)

f(x, y), thì cng đc xem nh vit (f x) y, ngha là kt
qu ca vic áp dng f cho x là mt hàm đ áp dng cho y. Ta vit x

(y

f (x, y)).
Mt cách tng quát cho hàm n bin f (x
1
, x
2
,..., x
n
) :
x
1



(x
2

(x
3


...(x
n

f (x
1
, ...,x
n
)) ...))
Chng hn xét hàm mult đc đnh ngha nh sau :
mult x y = x * y
Nu hàm mult đc gi vi hai đi s, thì mult s tính tích s ca hai đi s này, theo
ngha thông thng. Tuy nhiên trong ngôn ng Miranda, mult có th đc xem nh hàm
mt tham đi (đi th nht x), kt qu s là mt hàm khác, áp dng cho mt tham đi (đi
th hai y).
18 LP TRÌNH HÀM
Mt cách tng quát, mt hàm nào đó có nhiu hn mt tham đi có th đc tham đi
hóa tng phn. Chng hn ta đnh ngha hàm triple đ nhân 3 mt s :
triple x = 3 * x
Nhng cng có th đnh ngha li hàm triple theo cách currying :
triple = mult 3
Li gi :

triple 7
cho kt qu là 21 vì dn đn li gi mult 3 7.
I.2.6. Khái nim v bc ca hàm
Trong phn ln các ngôn ng mnh lnh, các đi tng c s (bin và hng) đc x lý
khác vi hàm. Chúng có th đc đc vào (t bàn phím, t tp...), đa ra (màn hình, máy in,
tp...), trao đi giá tr vi nhau, nhng hàm ch có th đc gi hoc đc kích hot (invoke).
Các đi tng c s đc gi là có bc 0, các hàm có các tham đi là nhng đi tng c
s đc gi là các hàm bc 1. Mt hàm bc n nhn các tham đi bc nh hn n, đ có các
biu thc bc cao hn.
Khái nim v bc đc Russel (Russell's Paradox) và Whitehead (Principia Mathematica
~1900) đ xut nhm loi b nhng nghch lý (paradox) thng hay gp trong lý thuyt tp hp
ca Cantor, chng hn nghch lý v ngi th co (barber paradox), bng cách làm gim tt c
các kh nng xy ra vòng lun qun. Trong lôgic v t bc mt, ch có th s dng các lng t
áp dng cho các cá th (các bin). Còn trong lôgic v t bc hai, ngi ta có th lng t hoá
các v t...
Trong các ngôn ng hàm, hàm luôn luôn đc x lý nh nhng đi tng đc truyn
tham đi đ tr v kt qu và đc lu gi trong các cu trúc d liu. Mt hàm nhn mt hàm
khác nh là mt tham đi đc gi là hàm bc cao. Các hàm bc cao mang li tính hiu qu
và là nn tng (corner-stone) ca lp trình hàm.
 hiu bn cht ca mt hàm bc cao, ngi đc có th t tìm giá tr kt qu ca
answer t các lnh 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 nhng hàm có nhiu hn mt tham đi đu là các hàm bc
cao. Chng hn t hàm th vin member x L kim tra danh sách L có cha phn t x
không (kt qu là true hoc false), ta có th áp dng đ đnh ngha 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 phi» (foldr right) dùng đ tính toán trên
các phn t ca mt danh sách. Miranda có hàm foldr đc đnh ngha nh sau :

NGUYÊN LÝ LP TRÌNH HÀM 19
foldr op k [] = k
foldr op k (a:L) = op a (foldr op k L)
Li gi hàm foldr :
foldr (+) 0 [2, 4, 7]
tng đng vi :
(2 + (4 + (7 + 0)))
--> 13
Chú ý các phép toán trung t trong ngôn ng Miranda, nh phép cng + trên đây, đc
xem là tham đi và phi đc đt trong mt cp du ngoc.
Ta có th đnh ngha các hàm x lý danh sách s dng hàm foldr. Chng hn hàm tính
tng tt c các phn t ca mt danh sách theo kiu currying nh sau :
sumlist = foldr (+) 0
Hàm sumlist có mt tham đi là danh sách s cung cp ba tham đi cho hàm foldr
nhng kt qu là do hàm op có mt tham đi tr v.
sumlist [2, 9, 52]
--> foldr (+) 0 [2, 9, 52]
--> 63
Mt cách tng t ta đnh ngha hàm tính tích ca tt c các phn t trong mt danh
sách :
product = foldr (*) 1
Hàm :
and = foldr (&) true

gi phép « và» lôgích cho tt c các phn t ca mt danh sách. Kt qu phép gi hàm and
L là true nu mi phn t ca danh sách L đu là true, kt qu là false nu ngc li.
Hàm nghch đo mt danh sách :
reverse = foldr postfix []
where postfix a L = L ++ [a]
Các ngôn ng hàm có nhiu hàm bc cao tin đnh, và ngi lp trình cng có th t đnh
ngha nhng hàm bc cao mnh và tng quát nh hàm foldr va xét.
Mt hàm bc cao quan trng khác là map f L vi f là mt hàm và L là mt danh sách.
Hàm map tr v kt qu là mt danh sách mi gm các phn t ca L đã đc áp dng 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 gi triple cho tt c các phn t ca 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 LP TRÌNH HÀM
I.2.7. Kiu và tính đa kiu
Các ngôn ng hàm có mt h thng kiu d liu hoàn toàn khác vi các ngôn ng mnh
lnh. Trong các ngôn ng mnh lnh (nh Pascal, C, Ada..), tính đnh kiu tnh cht ch
(static strong typing) bt buc ngi lp trình phi mô t kiu cho mi bin dùng đn. Sau khi
khai báo, ngi s dng không đc thay đi kiu d liu ca bin trong khi chy chng
trình.
Các ngôn ng hàm thng không s dng đnh kiu. Tuy nhiên mt s ngôn ng hàm li
s dng đnh kiu đng (dymamic typing) hoc đnh kiu n cht ch (implicit strong typing)
(thng gp trong ngôn ng Fortran).
Nh vy, các ngôn ng hàm to ra đc tính mm do v các hàm đa kiu (polymorphic
functions) vi li gi có các tham đi có các kiu d liu khác nhau.
Miranda s dng ch đ đnh kiu tnh cht ch tng minh (implicit static strong
typing). Mi bin mi biu thc đu có mt kiu đc xác đnh tnh. Có ba kiu tin đnh
trong Miranda là num, bool và char. Kiu num không phân bit s nguyên hay s thc.

Kiu bool có hai giá tr hng là true và false. Kiu char gm các ký t ASCII. Mt
hng ký t đc đt gia cp du nháy đn và s dng quy c tng t ngôn ng C, nh
’x’, ’&’, ’\n’, v.v...
Nu T là mt kiu, thì [T] là mt kiu danh sách gm các phn t kiu T. Ví d danh
sách sau đây :
[[1, 2], [2, 3], [4, 5]]
có kiu [[num]] vì đây là mt danh sách gm các phn t là danh sách các s. Mt hng
chui có dng [ char ], chng hn chui «bonjour» đc vit trong Miranda [’b’,
’o’, ’n’, ’j’, ’o’, ’u’, ’r’].
Nu T1, ..., Tn là các kiu nào đó, n>0, thì (T1, ..., Tn) là mt kiu bn ghi, hay b n
phn t bt k. Ví d b 3 (true, ”Honda”, 7331) có kiu (bool, [char], num).
Nu T1 và T2 là hai kiu nào đó, thì T1->T2 là mt kiu hàm vi tham đi có kiu T1
và giá tr tr v có kiu T2. Ví d hàm triple va đnh ngha trên đây có kiu :
num −> num
Chú ý phép −> có kt hp phi (right associative).
Ngi lp trình có th s dng các khai báo đnh ngha hàm. Ví d, hàm mult đc khai
báo kiu currying nh sau :
mult :: num −> (num −> num)
Có ngha rng mult đc xem là hàm có mt tham đi tr v mt hàm khác có kiu :
num −> num.
Du :: trong khai báo hàm đc đc «có kiu là » (is of type). Ví d :
sq :: num -> num
sq n = n * n
Tuy nhiên, vic khai báo kiu hàm là không cn thit. Các ngôn ng hàm thng có kh
nng uy din kiu t đng nh mt b kim tra kiu (type checker). Khi không khai báo kiu
hàm, chng hn nu ch đnh ngha hàm triple bi :
triple x = 3 * x

NGUYÊN LÝ LP TRÌNH HÀM 21
thì b kim tra kiu s suy ra rng x phi là mt s, vì x là mt tha s trong mt phép nhân.

B kim tra kiu cng suy ra đc rng kt qu ca triple x phi là mt s, do đó kiu
ca hàm này phi là :
triple :: num −> num
Tính đa kiu là yu t rt quan trng trong nhiu ngôn ng hàm. Vi mi tham bin hình
thc (formal parameters), mt hàm đa kiu có th chp nhn li gi tng ng vi nhiu
tham đi thc s (actual parameters) có các kiu khác nhau. Khác vi các th tc trong Ada,
mt hàm đa kiu là mt hàm đn (single function), không phi là các hàm bi (multiple
functions) có cùng tên. Ví d xét hàm đa kiu pair sau đây tr v mt danh sách t hai tham
đi là hai phn t :
pair x y = [x, y]
Hàm này có th đc s dng nh sau :
pair 1 2
--> [1, 2]
pair true false
--> [true, false]
pair [] [2]
--> [[], [2]]
Hàm pair đc đnh ngha đn, nhng có th s dng nhiu kiu tham đi khác nhau,
nh bool, num, [num], v.v..., sao cho c hai tham đi đu phi có cùng mt kiu. Nh vy,
kiu ca hàm pair không th đc din t nh kiu ca hàm triple va đnh ngha 
trên, vì rng các kiu ca các tham đi trong pair là không c đnh. Ngi ta đa ra gii
pháp s dng các bin kiu đc thù (generic type variables). Bên trong mt khai báo kiu,
mt bin kiu có th ch đnh mt kiu nào đó, cùng mt bin kiu cho phép ch đnh cùng
mt kiu xuyên sut c khai báo.
Miranda s dng các ký hiu *, **, *** đ ch đnh kiu tu ý. Ví d hàm đng nht id
(identity) đc đnh ngha nh sau :
id x = x
Hàm id có khai báo kiu nh sau :
id :: * -> *
có ngha là id có nhiu kiu. Sau đây là mt s khai báo kiu ca các hàm đã đc đnh

ngha trên đây :
fac :: num -> num
ack :: num -> num-> num
sum :: [num] -> num
month :: [char] -> bool
reverse :: [*] -> [*]
fst :: (*, **) -> *
snd :: (*, **)-> **
foldr :: (*-> ** -> **) -> ** -> [*] -> **
perms :: [*] -> [[*]]
22 LP TRÌNH HÀM
Các quy tc đnh ngha kiu nh trên làm tránh đc sai sót khi s dng hàm không đúng,
chng hn :
pair 1 true || các tham đi phi cùng kiu
5 + (pair 1 2) || kiu kt qu là mt danh sách, không là mt s.
Trong ví d th hai, Miranda xem rng kiu ca (pair 1 2) là mt danh sách các s,
nên không th dùng đ làm s hng cho mt phép cng.
Dù các hàm có tha nhn các tham đi khác kiu và không cn khai báo kiu mt cách
tng minh, h thng kiu va mô t trên đây là cht ch và  dng tnh. S vi phm các quy
tc đnh kiu s dn đn các thông báo li khi dch (compile time error messages).
Tuy nhiên vic thêm các khai báo kiu hàm thng làm cho b kim tra kiu d vi phm
sai sót khi s dng hàm.
I.2.8. Tính hàm theo kiu khôn ngoan
Thông thng, khi gi tính giá tr mt hàm, các tham đi đc tính giá tr trc tiên, sau
đó mi tin hành thc hin tính toán trong hàm. Chng hn, li gi :
mult (fac 3) (fac 4)
yêu cu tính giá tr các hàm giai tha (fac 3) và (fac 4) trc, theo mt th t tùy ý,
sau đó mi thc hin hàm nhân :
mult 6 24
đ cho kt qu cui cùng là 144. Cách vit rút gn biu thc lúc đu thành dng đn gin hn

đc gi là phép rút gn theo th t áp dng (applicative order reduction). Chú ý rng phép
rút gn đc bt đu t các biu thc trong cùng nht (innermost expressions).
Tuy nhiên có mt cách khác đ rút gn mt biu thc là bt đu t biu thc ngoài nht
(outermost expression) và không tính giá tr các biu thc con (subexpressions) cho đn khi
cn dùng đn kt qu ca chúng.
Ngi ta gi cách này là rút gn theo th t thng (normal order reduction). Chng hn
biu thc :
mult (fac 3) (fac 4)
s đc rút gn thành :
(fac 3) * (fac 4)
sau đó thành :
6 * 24
đ nhn đc kt qu 144.
Nhng ngi lp trình có kinh nghim trên các ngôn ng mnh lnh có th ngh rng
phép rút gn theo th t áp dng là thun tin hn, nhng cách này cng có nhng điu bt
tin.
Gi s rng ta mun xây dng hàm cond nhn mt giá tr bool b nh là tham đi th
nht và tr v tham đi th hai ca nó nu b có giá tr true hoc tr v tham đi th ba nu
b là false :
cond b x y = x, if b
cond b x y = y, otherwise
hay gn hn :

NGUYÊN LÝ LP TRÌNH HÀM 23
cond true x y = x
cond false x y = y
Nu c ba tham đi ca cond đc tính giá tr trc khi hàm cond đc thc hin, thì
s xy ra hai trng hp :
− Mt trong các tham đi ca cond tham gia tính toán mt cách vô ích, dn đn kt qu
sai, chng hn cond (x=0) 0 (1/x) vi x=0.

− Nu vic tính toán biu thc tham đi là vô ích và không kt thúc thì s gây ra toàn b
biu thc b tính lp vô hn ln.
Trng hp th hai có th đc minh ha qua ví d sau : gi s ta cng s dng hàm
cond cho mt đnh ngha khác ca hàm fac :
fac n = cond (n = 0) 1 (n * fac (n - 1))
Nu tham đi th ba ca cond luôn luôn đc tính, thì hàm fac s không bao gi dng.
Bi vì li gi fac 1 kéo theo li gi fac 0, li gi fac 0 s kéo theo li gi fac − 1
và c th tip tc.
Nhng vn đ trên đã dn đn khái nim tính giá tr theo kiu khôn ngoan trong lp trình
hàm s dng phép rút gn theo th t thng. Ý tng ca phng pháp tính giá tr hàm theo
kiu khôn ngoan là ch tính giá tr các tham đi ca mt hàm khi các giá tr ca chúng là cn
thit, nhng biu thc nào không cn thit thì b qua.
S dng tính giá tr hàm theo kiu khôn ngoan, phép tính hàm fac 1 s đc rút gn
nh đc trình bày di đây. Vic tính giá tr kt thúc và cho kt qu đúng.  đây ta s dng
biu thc if dng gi ng (pseuclo-notation). Tham đi th ba ca cond s không đc
tính nu n bng 0, vì rng trong trng hp này, không cn tính tham đi na.
fac 1
--> cond (1=0) 1 (1*fac (1-1)) || gi fac
--> if (1=0) then 1 else (1*fac (1-1)) || gi 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))) || gi fac
--> 1*((if 1-1=0) then 1 else ((1-1)*fac ((1-1)-1)))
|| gi 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
Phng pháp tính hàm kiu khôn ngoan còn th hin mt li ích quan trng khác : cho
phép đnh ngha các cu trúc d liu vô hn theo kiu ý nim (conceptually infinite). Chng

hn trong Miranda, danh sách các s nguyên dng có th đc đnh ngha :
[1.. ]
Danh sách theo kiu đnh ngha này có th tham gia tính toán nh mi danh sách khác.
Chng hn :
hd [1.. ]
--> 1 || phn t th nht
hd (tl [1.. ])

×