Phần 3- Ngôn ngữ lập trình Pascal
ngôn ngữ lập trình pascal (tip theo)
Chơng 11. Kiểu tập hợp và kiểu mảng
11.1. Kiểu tập hợp (set type).
11.1.1. Định nghĩa và khai báo : Dữ liệu kiểu tập hợp là một tập hợp của
những dữ liệu thuộc một kiểu vô hớng đếm đợc.
Một kiểu tập hợp đợc định nghĩa bởi dạng sau :
trong đó kiểu_phần_tử là kiểu dữ liệu của các phần tử của tập hợp.
Khai báo biến kiểu tập hợp :
Cách 1 : Khai báo qua một tên kiểu đã đợc định nghĩa :
Ví dụ 1:
Type
Chu_so = Set of 0..9;
Chu_cai_lon = Set of 'A'..'Z';
Var
TapA : Chu_so;
TapH : Chu_cai_lon;
Cách 1 : Khai báo trực tiếp :
Ví dụ 2 :
Var TapS : Set Of Byte;
T1, T2 : Set Of (Xanh,Vang,Tim);
Ghi chú :
Biến tập hợp cho phép có tối đa 256 phần tử.
Nếu kiểu_cơ_sở là kiểu nguyên thì giá trị phải nằm trong đoạn từ 0 đến 255.
Chẳng hạn, Với khai báo :
Type Tap_so = Set of 100..256;
Khi dịch máy sẽ thông báo lỗi : Set base type out of range.
Hằng kiểu tập hợp đợc biểu diễn dới dạng liệt kê các phần tử nằm trong hai
dấu ngoặc vuông [ ]. Chẳng hạn : ['A','D','E'], [3,5..8] (tập các số nguyên 3,
5, 6, 7, 8) là các hằng kiểu tập hợp.
(Vị trí của các phần tử trong tập hợp không có ý nghĩa : [1,2]=[2,1])
54
TYPE
Tên_kiểu = SET OF kiểu_phần_tử;
Phần 3- Ngôn ngữ lập trình Pascal
Tập hợp rỗng đợc kí hiệu là [].
Có thể thực hiện phép gán trên các biến kiểu tập hợp.
Ví dụ 3 : Với các biến đợc khai báo ở ví dụ trên, ta có thể gán :
TapA := [1,2,7]; TapH := []; {Tập hợp rỗng}
T1 := [Vang,Tim];
11.1.2. Các phép toán trên tập hợp :
Phép toán quan hệ :
- Phép toán = (bằng) : Cho giá trị True nếu 2 tập hợp bằng nhau.
- Phép toán <> (khác) : Cho giá trị True nếu 2 tập hợp khác nhau.
- Phép toán <= (nhỏ hơn hoặc bằng) : A <= B cho giá trị True nếu A là tập
con của B.
- Phép toán >= (lớn hơn hoặc bằng) : A >= B cho giá trị True nếu B là tập
con của A.
Chú ý : Không có phép toán < (nhỏ hơn) và > (lớn hơn). Để kiểm tra tập hợp
A có thật sự nằm trong tập hợp B không ta sử dụng phép toán AND nh câu lệnh
sau đây :
If (A<>B) and (A<=B) then Write('A là tập con thật sự của B');
Phép toán IN (thuộc về) : Dùng để kiểm tra xem một biến hay một giá trị
có nằm trong tập hợp nào đó không ? Nếu phần tử đó có trong tập hợp thì
phép toán sẽ trả về giá trị True, ngợc lại cho giá trị False.
Ví dụ 4 :
'C' In ['A','C','D'] True
'E' In ['A','C','D'] False
Phép toán hợp, giao, hiệu : Gọi A, B là 2 dữ liệu cùng kiểu tập hợp.
- A+B là hợp của A và B : Tập hợp các phần tử thuộc A hoặc thuộc B.
- A*B là giao của A và B : Tập hợp các phần tử thuộc A và thuộc B.
- A-B là hiệu của A và B : Tập hợp các phần tử thuộc A và không thuộc B.
Ví dụ 5 : Với A:=[1,3,9]; B:=[9,2,5];
Ta có :
A+B = [1,2,3,5,9]
A*B = [9]
A-B = [1,3]
55
PASCAL Toán học
[1,2,5]
[]
IN
=
<>
<=
>=
+
*
-
{1,2,5}
=
\
Giáo trình tin học đại cơng
Ví dụ 6 : Viết chơng trình nhập vào một chữ cái. Xem xét chữ cái đó là nguyên
âm hay phụ âm ?
Program VIDU_6;
Uses Crt;
Var
TapChuCai, TapNguyenAm : Set Of Char;
Ch : Char;
Begin
TapChuCai :=['A'..'Z','a'..'z'];
TapNguyenAm :=['A','E','I','O','U'];
Repeat
Clrscr;
Write('Nhap mot chu cai: ');Readln(Ch);
Until Ch in TapChuCai;
If upcase(Ch) in TapNguyenAm then Writeln(Ch,' là nguyên âm')
Else Writeln(Ch,' là phụ âm');
Readln;
END.
11.2. Kiểu mảng (array type).
11.2.1. Mảng một chiều :
1. Khái niệm : Mảng (Array) là một kiểu dữ liệu có cấu trúc bao gồm một số
cố định các thành phần có cùng kiểu dữ liệu, có cùng một tên chung. Mỗi phần
tử của mảng có một đại lợng xác định vị trí tơng đối của phần tử đó so với các
phần tử khác trong mảng, gọi là chỉ số. Chẳng hạn, mảng a gồm 5 phần tử đợc
phân bố nh sau :
chỉ số
a a[1] a[2] a[3] a[4] a[5]
tên mảng
Công dụng của mảng là dùng lu trú một dãy số liệu có cùng một tính chất
nào đó, chẳng hạn :
- Họ tên của các nhân viên trong cùng một xí nghiệp.
- Điểm thi một môn học của các học sinh trong một lớp.
2. Định nghĩa kiểu dữ liệu mảng :
TYPE
tên_kiểu_mảng = ARRAY[m1 .. m2] OF kiểu_phần_tử;
Trong đó :
kiểu_phần_tử là kiểu của mỗi phần tử trong mảng, có thể là kiểu bất kỳ.
56
Phần 3- Ngôn ngữ lập trình Pascal
m1, m2 là hai hằng xác định phạm vi của chỉ số và m1 m2.
Kiểu dữ liệu của chỉ số không đợc là kiểu thực hay kiểu chuỗi, thờng là kiểu
nguyên, ký tự, logic, liệt kê hay đoạn con.
Ví dụ 7 :
Type
dayso = Array[0..49] Of byte;
HT = Array[1..50] Of string[20];
Theo định nghĩa trên ta có hai kiểu mảng :
Kiểu mảng thứ nhất có tên là dayso, gồm 50 phần tử cùng kiểu Byte, ứng với các
chỉ số 0, 1, 2, ..., 49. Chỉ số đầu (m1 = 0) và chỉ số cuối (m2 = 49) có thể bắt đầu
từ một con số bất kỳ và kết thúc ở một con số bất kỳ, miễn sao cho m2-m1+1=50
theo đúng yêu cầu tạo mảng có 50 phần tử. Thông thờng, nếu không bắt buộc, ta
chọn chỉ số đầu (m1) bằng 0 hoặc 1.
Kiểu mảng thứ hai có tên là HT, gồm 50 phần tử cùng kiểu dữ liệu là String[20],
ứng với các chỉ số từ 1 đến 50.
3. Khai báo biến kiểu mảng : Có hai cách khai báo :
Cách 1 : Khai báo qua một kiểu mảng đã đợc định nghĩa.
Ví dụ 8 : Với hai kiểu mảng đã đợc định nghĩa ở ví dụ 7, có thể khai báo :
Var a : dayso;
Ho_ten : HT;
Cách 2 : Khai báo trực tiếp.
Ví dụ 9 :
Var x : Array[0..99] Of Real;
y : Array[a..d] Of Integer;
4. Khai báo mảng có gán giá trị ban đầu : Turbo Pascal cho phép vừa khai
báo mảng, vừa gán giá trị ban đầu cho các phần tử mảng, chẳng hạn nh dới
đây :
Const x : Array[1..5] Of Integer = (5,7,12,14,6);
Khi đó x là một mảng gồm 5 phần tử cùng kiểu nguyên và có giá trị x[1]=5,
x[2]=7, x[3]=12, x[4]=14, x[5]=6.
Mặc dù từ khoá ở đây là const, nhng x lại đợc dùng nh là một biến, tức là
các phần tử của x có thể thay đổi giá trị đợc, chẳng hạn trong chơng trình ta có
thể gán :
x[1] := 4; x[2] :=3+25;
57
Giáo trình tin học đại cơng
5. Truy xuất một phần tử mảng : Các xử lý trên mảng đợc quy về xử lý từng
phần tử của mảng. Việc truy xuất một phần tử nào đó của biến mảng đợc
thực hiện qua tên biến mảng, theo sau là giá trị chỉ số để trong dấu [ ].
tên_biến_mảng[chỉ_số_phần_tử ]
Ví dụ 10 :
Var a : Array[1..25] Of Integer;
b : Integer;
...
a[1] := 1; {gán giá trị cho phần tử đầu tiên của mảng bằng 1}
b := a[25]; {gán giá trị b bằng giá trị phần tử cuối cùng của mảng}
Ghi chú :
Chỉ số của một phần tử mảng có thể là một biến, một hằng hay một biểu thức.
Có thể gán một biến mảng này cho một biến mảng khác cùng kiểu. Chẳng
hạn, với khai báo :
Var x, y : Array[1..15] Of Real;
Khi đó, lệnh :
x := y;
tơng đơng với lệnh :
For i:=1 to 15 do x[i] := y[i];
6. Nhập - xuất dữ liệu với mảng : Dùng câu lệnh FOR.
Các thủ tục nhập - xuất biến nh Writeln, Readln, ... không thể truy xuất
thẳng biến mảng mà phải thông qua từng thành phần của của nó.
Ví dụ 11 : Viết chơng trình nhập vào từ bàn phím một dãy số nguyên MI và
xuất ra màn hình:
Các phần tử của dãy MI theo thứ tự ngợc.
Các số đối của các phần tử của dãy MI.
Program VIDU_11;
Uses Crt;
Const Max = 100;
Var MI : Array[1..Max] Of Integer;
i, n : Byte;
Begin
Repeat
Clrscr;
Write('Nhập số phần tử của dãy ( < ', Max , ' ): ');
Readln(n);
Until (n>0) and (n <= Max);
58
Phần 3- Ngôn ngữ lập trình Pascal
{nhập mảng}
For i:=1 to n do
Begin
Write('Phần tử thứ ',i,': ');
Readln(MI[i]);
End;
Writeln('------------------------');
Writeln('In dãy theo thứ tự ngợc :');
For i := n downto 1 do write(MI[i],#32);
Writeln;
Writeln('In các số đối của dãy :');
For i:=1 to n do write(-MI[i],#32);
Readln;
End.
7. Các bài toán cơ bản về mảng :
Bài toán 1 : Tìm phần tử nhỏ nhất của mảng.
Ví dụ 12 : Viết chơng trình nhập vào 10 phần tử của mảng a các số nguyên.
Tìm và in phần tử nhỏ nhất của mảng a lên màn hình.
Program VIDU_12;
Uses Crt;
Const N = 10;
Type Dayso = Array[1..N] Of Integer;
Var a : Dayso;
i : Byte;
min : Integer;
Begin
Clrscr;
{nhập mảng}
For i:=1 to N do
Begin
Write('Nhap a[ ',i,' ] : ');
Readln(a[i]);
End;
Writeln('------------------------');
{tìm số nhỏ nhất}
min := a[1];
For i := 2 to N do
if a[i] < min then min := a[i];
Writeln('Phần tử nhỏ nhất là : ',min);
Readln;
End.
Ta có giải thuật tơng tự đối với bài toán tìm phần tử lớn nhất của mảng.
Bài toán 2 : Sắp xếp các phần tử trong mảng theo thứ tự tăng dần.
59
Giáo trình tin học đại cơng
Giải thuật BUBBLE SORT (còn có tên là phơng pháp sắp xếp nổi bọt) : Ta coi
các số cần sắp là trọng lợng của các bọt khí trong nớc. Bọt khí nào nhẹ sẽ nổi
lên trớc, những bọt nào nặng sẽ nổi lên sau nhng không thể nổi lên cao quá bọt
khí nhẹ hơn nó. Một cách hình ảnh ta có ví dụ minh hoạ sau :
Sắp xếp tăng dần mảng : 44 55 12 42 94 18 6 67
Bớc 0 1 2 3 4 5 6 7
44 6 6 6 6 6 6 6
55 44 12 12 12 12 12 12
12 55 44 18 18 18 18 18
42 12 * 55 44 42 42 42 42
94 42 18 * 55 44 44 44 44
18 94 42 42 * 55 55 55 55
6 * 18 * 94 67 67 67 67 67
67 67 67 * 94 94 94 94 94
* Chơng trình minh hoạ
Program Sap_xep;
Uses Crt;
Const N = 8;
Var a : Array[1..N] Of Integer;
i, j ,Tam : Integer; { Tam là biến trung gian }
Begin
Clrscr;
Writeln('PP. BUBLE SORT SAP XEP MANG THANH DAY TANG DAN');
Writeln('-----------------------------------------------------------------------------------');
(* Nhập số liệu a *)
For i:=1 to N do
Begin
Write('a[', i ,' ] = ');
Readln(a[i]);
End;
(* Sắp xếp *)
For i:=2 to N do
For j:=N downto i do
if a[j-1] > a[j] then { đổi chỗ a[j-1] và a[j] }
Begin
Tam := a[j-1];
a[j-1] := a[j];
a[j] := Tam;
End;
(* In ra kết quả *)
Writeln('Dãy sau khi đã sắp xếp : ');
60
Phần 3- Ngôn ngữ lập trình Pascal
For i:=1 to N do Write(a[i]:6);
Readln;
End.
Giải thuật sắp xếp trên có thể viết theo vòng lặp REPEAT nh sau:
i := 2;
Repeat
doi := false;
For j := N downto i do
if a[j-1] > a[j] then
Begin
Doi :=true; {doi là biến Boolean}
Tam := a[j-1];
a[j-1] := a[j];
a[j] := Tam;
End;
i:=i+1;
Until Not Doi;
Kết quả chạy chơng trình hiện ra trên màn hình :
PP. BUBBLE SORT SAP XEP MANG THANH DAY TANG DAN
---------------------------------------------------------------------------------------
a[1] = 44
a[2] = 55
a[3] = 12
a[4] = 42
a[5] = 94
a[6] = 18
a[7] = 6
a[8] = 67
Dãy sau khi đã sắp xếp :
6 12 18 42 44 55 67 94
Ta có giải thuật tơng tự cho việc sắp xếp mảng theo thứ tự giảm dần.
Bài toán 3 : Tìm kiếm một phần tử bất kỳ trong mảng đã đợc sắp có thứ tự.
Giải thuật Binary Search (Tìm kiếm nhị phân) :
Giả sử ta có mảng đã sắp xếp theo thứ tự tăng dần, cần tìm phần tử x có
trong mảng không ?
Giải thuật đợc thực hiện nh sau :
Bớc 1: Phạm vi tìm kiếm là trong toàn bộ mảng.
Bớc 2: Lấy phần tử giữa của phạm vi cần tìm kiếm (gọi là y) và so sánh với x.
61
Giáo trình tin học đại cơng
Nếu x = y thì giải thuật kết thúc và thông báo đã tìm thấy.
Nếu x < y thì phạm vi tìm kiếm mới là các phần tử nằm trớc y.
Nếu x > y thì phạm vi tìm kiếm mới là các phần tử nằm sau y.
Bớc 3: Nếu tồn tại phạm vi tìm kiếm thì lặp lại bớc 2, ngợc lại thông báo không
tìm thấy, giải thuật kết thúc không thành công.
Sau đây là ví dụ minh hoạ ý tởng của giải thuật :
Cần tìm phần tử x = 4 trong mảng : 1 3 4 5 7 10 12 15
l 1 l 1 l
3 j 3 *
4 r 4 j 4 ==> Tìm thấy
j 5 *
7 r
10
12
r 15
* Chơng trình minh hoạ
Program BINARY_SEARCH;
Uses Crt;
Const N = 8;
Var
a : Array[1..N] Of Integer;
l,r,i,j : Byte;
x : Integer;
Timco : Boolean;
Begin
Clrscr;
Writeln('Nhap day theo thu tu tang : ');
For i:=1 to N do
Begin
Write('Phan tu thu ',i,': '); Readln(a[i]);
End;
Writeln(' * * *');
Write('Nhap x = '); Readln(x);
Timco:=False;
l:=1; r:=n; {l, r cho phạm vi tìm kiếm}
While Not(Timco) and (l<=r) do
Begin
j := (l+r) div 2; {phần tử giữa}
If a[j] < x then l:=j+1;
If a[j] > x then r:=j-1;
If a[j] = x then Timco:=True;
End;
Writeln(' ');
62
Phần 3- Ngôn ngữ lập trình Pascal
If Timco then Write('Da tim co phan tu thu ',j,' bang ',x)
Else Write('Khong co phan tu nao trong day bang ',x);
End.
11.2.2. Mảng hai chiều (ma trận) :
1. Định nghĩa mảng hai chiều : Mảng hai chiều, còn gọi là ma trận, là sự mở
rộng trực tiếp của mảng một chiều. Một kiểu mảng hai chiều đợc định nghĩa
bởi dạng sau :
TYPE
tên_kiểu_mảng = ARRAY[ n1 . . n2, m1 . . m2 ] OF kiểu_phần_tử;
Trong đó :
n1, n2 (n1 n2) là các hằng xác định phạm vi của chỉ số thứ nhất, gọi là chỉ
số dòng. Tơng tự m1, m2 (m1 m2) là các hằng xác định phạm vi của chỉ số
thứ hai, gọi là chỉ số cột.
Giống nh mảng một chiều, kiểu dữ liệu của các chỉ số không đợc là kiểu
thực hay chuỗi.
Ví dụ 13 :
Type mang1 = Array[1..2, 0..2] Of Integer;
mang2 = Array[a..c,1..3] Of String[10];
2. Khai báo mảng hai chiều :
Cách 1 : Khai báo thông qua một kiểu mảng hai chiều đã đợc định nghĩa :
Ví dụ 14 : Với hai kiểu mảng đã đợc định nghĩa ở ví dụ 13, có thể khai báo :
Var X : mang1;
Y : mang2;
Kết quả ta nhận đợc hai mảng hai chiều :
- Mảng X gồm 6 phần tử cùng kiểu dữ liệu nguyên :
X[1,0], X[1,1], X[1,2]
X[2,0], X[2,1], X[2,2]
- Mảng Y gồm 9 phần tử cùng kiểu chuỗi String[10] :
Y[a,1], Y[a,2], Y[a,3]
Y[b,1], Y[b,2], Y[b,3]
Y[c,1], Y[c,2], Y[c,3]
Cách 2 : Khai báo trực tiếp :
Ví dụ 15 :
Hai mảng X và Y ở ví dụ 14 có thể đợc khai báo trực tiếp nh sau :
Var X : Array[1..2,0..2] Of Integer;
63
Giáo trình tin học đại cơng
Y : Array[a..c,1..3] Of String[10];
3. Các thao tác trên mảng hai chiều :
Để xác định một phần tử trong mảng hai chiều, ta viết :
tên_biến_mảng[chỉ_số_1, chỉ_số_2]
Ví dụ 16 : Với hai mảng X, Y ở ví dụ 14, ta có thể dùng các lệnh gán sau :
X[1,0] := 5;
X[2,1] := X[1,1] - 20;
Y[a,1] :=TOAN;
Để nhập dữ liệu cho một mảng hai chiều, ta phải dùng hai vòng lặp duyệt
theo hai chỉ số, chẳng hạn muốn nhập dữ liệu cho mảng X ở ví dụ 1, ta viết :
For i:=1 to 2 do
for j:=0 to 2 do
Begin
Write(Nhập phần tử hàng ,i, cột ,j, : );
Readln(X[i,j]);
End;
Để in mảng X lên màn hình, trình bày giống nh cách viết ma trận, mỗi hàng
in trên một dòng, ta dùng lệnh :
For i:=1 to 2 do
Begin
{in hàng thứ i}
for j:=0 to 2 do write(X[i,j]:3);
{xuống dòng, chuẩn bị in dòng tiếp theo}
Writeln;
End;
Ví dụ 17 : Viết chơng trình đọc số liệu vào một bảng 2 chiều rồi tính và in ra
tổng của từng cột và tổng của cả bảng.
Program Vidu_17;
Uses Crt;
Type
Matrix = Array[1..3,1..4] Of Integer;
Var
a : Matrix;
i : 1..3; j: 1..4;
tong_cot, tong: Integer;
Begin
64
Phần 3- Ngôn ngữ lập trình Pascal
Clrscr;
(* Nhập số liệu vào bảng *)
For i:=1 to 3 do (* Vòng lặp cho từng hàng (chiều thứ 1) *)
For j:=1 to 4 do (* Vòng lặp cho từng cột (chiều thứ 2) *)
Begin
Write('Hãy cho biết phần tử a[', i , ',' , j ,'] = ');
Readln(a[i,j]);
End;
(* Tính và in các tổng *)
Tong:=0;
For j:=1 to 4 do
Begin
Tong_cot:=0;
For i:=1 to 3 do Tong_cot:=Tong_cot + a[i,j];
Writeln('Tổng các số trên cột ',j,' là: ',Tong_cot);
Tong:=Tong + Tong_cot;
End;
Writeln('Tổng tất cả các số trong bảng là: ', tong);
End.
Bài tập
[11.1] Viết chơng trình nhập N số nguyên trong phạm vi từ 0 đến 255 từ bàn
phím. In ra màn hình tập các số chẵn, tập các số lẻ và cho biết có bao nhiêu số
chẵn, bao nhiêu số lẻ (các số trùng nhau chỉ kể một lần).
Ví dụ : Nhập : 2, 3, 5, 2, 4, 8.
Xuất : Các số chẵn là : 2, 4, 8 Có 3 số chẵn.
Các số lẻ là : 3, 5 Có 2 số lẻ.
[11.2] Viết chơng trình nhập vào một dãy số và xuất ra màn hình các thông tin sau:
1. Phần tử lớn nhất của dãy.
2. Phần tử nhỏ nhất của dãy.
3. Tổng các phần tử của dãy.
[11.3] Viết trong cùng một chơng trình thực hiện các yêu cầu sau:
1. Nhập vào từ bàn phím một dãy số nguyên MI có 10 phần tử và một số
nguyên x thoả tính chất: kí số tận cùng của x bằng 6 và -180 < x 1200.
2. Xuất ra màn hình số phần tử trong dãy MI thoả tính chất: Có 2 chữ số và
chia đúng cho x
3. Tính tích của các phần tử MI[i] thoả tính chất: -10 < MI[i] < 20
[11.4] Viết chơng trình đổi một số nguyên dơng từ hệ cơ số 10 sang hệ cơ số 2.
[11.5] Viết chơng trình xuất ra màn hình bảng sau (tam giác PASCAL), biết số
hàng đợc nhập từ bàn phím.
1
1 1
65
Giáo trình tin học đại cơng
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
[11.6] Viết chơng trình đổi năm dơng lịch sang âm lịch (Theo hệ đếm can chi).
Hớng dẫn :
+ Trớc hết ta dùng các mã số sau:
Mời can :
Canh Tân Nhâm Quý Giáp
ất
Bính Đinh Mậu Kỷ
0 1 2 3 4 5 6 7 8 9
Mời hai chi :
Thân Dậu Tuất Hợi Tý Sửu Dần Mão Thìn Tỵ Ngọ Mùi
0 1 2 3 4 5 6 7 8 9 10 11
+ Công thức :
CAN năm = số d của Năm chia cho 10
CHI năm = số d của Năm chia cho 12
Ví dụ: Năm 1497
CAN năm = 1497 MOD 10 = 7 Đinh
CHI năm = 1497 MOD 12 = 9 Tỵ
Vậy năm 1497 là năm Đinh Tỵ.
[11.7] Viết chơng trình nhập một danh sách N ( 100) học sinh gồm họ tên và
điểm thi hai môn Toán, Văn. Tính điểm trung bình (dtb) và xếp loại các học
sinh nh sau : loại giỏi nếu dtb 9, loại khá nếu 7 dtb < 9, loại trung
bình nếu 5 dtb <7, và loại kém nếu dtb < 5.
In danh sách lên màn hình, mỗi học sinh trên một dòng, gồm : họ tên, dtb,
kết quả xếp loại, và in số học sinh có điểm trung bình 10. Yêu cầu danh sách đ-
ợc sắp theo thứ tự giảm của dtb.
[11.8] Viết chơng trình nhập vào từ bàn phím một dãy 10 số nguyên và một số
nguyên X. Dò tìm xem trong dãy có hai phần tử liên tiếp nào mà tổng của
chúng bằng X không ?
[11.9] Viết chơng trình nhập vào từ bàn phím một dãy số nguyên. Xuất ra màn
hình tần suất (số lần xuất hiện) của các số trong dãy số đó.
Ví dụ :
Nhập số phần tử của dãy N = 6
a[1] = 2
66
Phần 3- Ngôn ngữ lập trình Pascal
a[2] = 3
a[3] = 4
a[4] = 2
a[5] = 2
a[6] = 5
Kết quả :
- Tần suất của 2 là : 3
- Tần suất của 3 là : 1
- Tần suất của 4 là : 1
- Tần suất của 5 là : 1
[11.10] Viết chơng trình tạo và in ra trên màn hình ma trận đơn vị cấp 10.
[11.11] Viết chơng trình nhập vào từ bàn phím 1 ma trận vuông và xuất ra màn
hình tổng các phần tử trên đờng chéo chính.
67
Giáo trình tin học đại cơng
Chơng 12. Chơng trình con
12.1. Khái niệm về chơng trình con (subprogram).
Chơng trình con là một đoạn chơng trình có tên và có chức năng giải quyết
một vấn đề chuyên biệt mà chơng trình chính cần sử dụng nhiều lần, hoặc thực
hiện một số lần ứng với các giá trị khác nhau của tham số. Chẳng hạn, viết ch-
ơng trình tính tổ hợp chập m của n theo công thức :
C =
)!(!
!
mnm
n
C
m
n
=
Thay vì trong chơng trình phải viết ba đoạn chơng trình tính giai thừa : n!,
m! và (n-m)!, ta chỉ cần viết một đoạn chơng trình con tính k! tổng quát và đặt
tên là GiaiThua(k). Sau đó, mỗi lần muốn tính giai thừa, ta chỉ gọi lại đoạn ch-
ơng trình này và đặt giá trị k cho thích hợp. Lúc này, để tính tổ hợp trên ta chỉ
cần dùng một lệnh :
C := GiaiThua(n)/(GiaiThua(m)*(GiaiThua(n-m)).
Việc sử dụng chơng trình con không chỉ có tác dụng làm cho chơng trình bớt
rờm rà, dài dòng mà còn đặc biệt có ý nghĩa trong việc tổ chức chơng trình.
Chẳng hạn, khi phải giải quyết các bài toán lớn, ta có thể chia nó ra nhiều bài
toán nhỏ. Mỗi bài toán nhỏ đợc giải quyết riêng rẽ bằng một chơng trình con
(sub-routine hay sub-program). Các lập trình viên có thể làm việc độc lập với
nhau trên các chơng trình con, ngời quản lý chung chỉ cần tập hợp và sử dụng
các chơng trình con để phát triển chơng trình chính. Phơng pháp này cho phép
nhìn nhận và giải quyết vấn đề một cách tổng quát, không bị sa vào các chi tiết
nhỏ. Đây là t tởng cơ bản của lập trình có cấu trúc : thiết kế từ tổng quát tới chi
tiết (Top - Down design method).
Trong TURBO PASCAL có hai loại chơng trình con là hàm và thủ tục.
12.2. Hàm : Khái niệm hàm cũng gần giống khái niệm hàm của toán học,
nghĩa là, hàm nhận vào các giá trị đầu vào và cho ra một giá trị đầu ra duy
nhất. Giá trị đầu vào của hàm gọi là tham số của hàm.
12.2.1. Dạng tổng quát của khai báo hàm :
FUNCTION Tên_hàm(tham_số_hình_thức) : kiểu_kết_quả;
... {Khai báo của riêng hàm, biến ở đây gọi là biến cục bộ (hay địa phơng)}
Begin
... {Các lệnh của riêng hàm}
End;
68
Phần 3- Ngôn ngữ lập trình Pascal
Trong đó :
Tên_hàm là một tên để ký hiệu hàm. Kết quả thực hiện hàm là giá trị lu
trong Tên_hàm.
Tham_số_hình_thức là một dãy các tên biến cùng với mô tả kiểu của nó.
Trong tham_số_hình_thức, các biến đợc khai báo kiểu bằng hai cách:
Cách 1 : danh_sách_biến> : kiểu_dữ_liệu. Các biến trong danh_sách_biến
này gọi là tham trị.
Cách 2 : Var danh_sách_biến : kiểu_dữ_liệu. Các biến trong danh_sách_
biến này gọi là tham biến.
Chú ý :
Phải xác định kiểu_kết_quả cho tên_hàm.
Sau khi đã tính đợc kết quả của hàm, ta cần phải xuất giá trị này cho ch-
ơng trình gọi, tức là phải có lệnh gán giá trị cho tên_hàm thông qua câu lệnh
gán giá trị : tên_hàm := Biểu_thức;
Ví dụ 1:
- Sau đây là khai báo tiêu đề để tính hàm k!, nhận vào k là một số nguyên d-
ơng và trả kết quả cũng là số nguyên.
Function GiaiThua(k : Byte) : LongInt;
- Sau đây là khai báo tiêu đề hàm tính a
n
, nhận vào a là một số thực, n là số
nguyên và trả ra kết quả là số thực.
Function LuyThua(a : Real, n : Byte) : Real;
Ví dụ 2: Chơng trình con sau có dạng hàm dùng để tính hàm tang.
Function tg(x:Real) : Real;
Begin
tg:= sin(x)/cos(x);
End;
12.2.2. Lời gọi hàm : Hàm đợc gọi nh là một toán hạng trong biểu thức. Lời
gọi hàm nh sau :
tên_hàm(tham_số_thực_sự)
Trong đó tham_số_thực_sự là một dãy các biểu thức tơng ứng với các biến
trong tham_số_hình_thức. Nếu tham_số hình_thức là tham biến, thì tham số
thực sự tơng ứng phải là tên biến. Nếu tham số hình thức là tham trị thì tham
số thực sự tơng ứng có thể là biểu thức.
Ví dụ 3 : Nh hàm định nghĩa ở ví dụ 2 ở trên, ta có thể có lời gọi :
y:= tg(x*x+2);
69
Giáo trình tin học đại cơng
Ví dụ 4 : Viết chơng trình tính tổ hợp chập m của n, với m và n là hai số
nguyên dơng đợc nhập từ bàn phím.
Program Vidu_4;
Uses Crt;
Var n, m : Integer; {các khai báo biến này có thể đặt trớc hoặc sau khai báo hàm }
C : Real;
Function GiaiThua(k: Integer) : LongInt;
Var i : Integer;
GT : LongInt;
Begin
GT :=1;
For i:=2 to k do GT := GT *i;
GiaiThua := GT;
End;
Begin
Repeat
ClrScr;
Write(Nhập m và n nguyên dơng : );
Readln(m,n);
Until (m > 0) and (m <= n);
C := GiaiThua(n)/( GiaiThua(m)* GiaiThua(n-m));
Writeln(Tổ hợp chập ,m, của ,n, là : ,C:1:4);
Readln
End.
12.3. Thủ tục : Thủ tục chỉ là một trờng hợp đặc biệt của hàm. Thủ tục là
một hàm mà không có kết quả trả về. Hơn nữa, lời gọi thủ tục không thể xuất
hiện trong biểu thức. Ngoại trừ hai điểm này, hàm và thủ tục tơng tự về mọi
mặt.
12.3.1. Dạng tổng quát của khai báo thủ tục
PROCEDURE Tên_thủ_tục([tham_số_hình_thức]);
... {Khai báo của riêng thủ tục}
Begin
... {Các lệnh của thủ tục}
End;
Trong đó :
Tên_thủ_tục là một tên để ký hiệu thủ tục.
Tham_số_hình_thức là một dãy các tên biến cùng với mô tả kiểu của nó.
Trong tham_số_hình_thức các biến đợc khai báo kiểu dữ liệu của nó bằng
70
Phần 3- Ngôn ngữ lập trình Pascal
hai cách nh đối với chơng trình con FUNCTION, tham_số_hình_thức có thể
không có.
Chú ý : Trớc khi thoát khỏi thủ tục, không cần gán giá trị cho Tên_thủ_tục.
Ví dụ 5 : Chơng trình con Max sau đây thực hiện việc tìm số lớn nhất trong hai
số thực cho trớc.
Procedure Max(x,y: Real; Var z:Real);
Begin
If x > y Then z := x
else z := y;
End;
12.3.2. Lời gọi thủ tục : Thủ tục đợc gọi nh một lệnh đơn trong chơng trình.
Dạng gọi thủ tục nh sau :
Tên_thủ_tục([tham_số_thực_sự])
Trong đó tham_số_thực_sự là một dãy các biểu thức tơng ứng với các biến
trong tham_số_hình_thức. Nếu tham_số_hình_thức là tham biến, thì tham_số_
thực_sự tơng ứng phải là tên biến. Nếu tham_số_hình_thức là tham trị thì
tham_số_thực_sự tơng ứng có thể là biểu thức.
Kết quả của lời gọi thủ tục là giá trị nhận đợc ở các tham biến.
Ví dụ 6 : Với chơng trình con Max ở ví dụ 5 và a, b là hai biến có giá trị đã đợc
xác định trớc đó. Khi đó, lời gọi sau :
Max(a*a-3,b+2,c);
cho kết quả là giá trị lớn nhất giữa hai giá trị a*a-3 và b+2, kết quả đợc lu trong
tên biến c.
12.4. Ghi chú.
1. Các tham số có mặt trong tham_số_hình_thức> và tham_số_thực_sự phải t-
ơng ứng với nhau về số lợng tham số và kiểu dữ liệu.
2. Các biến trong chơng trình có sử dụng chơng trình con đợc chia thành 2 loại
:
Biến toàn cục : Đợc khai báo ở ngoài chơng trình con trong phần khai báo của
chơng trình chính; đợc hiểu và sử dụng mọi nơi trong chơng trình.
Biến cục bộ : Đợc khai báo bên trong Ctrình con; chỉ đợc hiểu và sử dụng bên
trong Ctrình con đó.
3. Trong trờng hợp, có một biến toàn cục và một biến cục bộ có cùng tên thì :
Nếu sử dụng tên biến đó trong thân chơng trình con thì biến đó đợc hiểu là
biến cục bộ.
71
Giáo trình tin học đại cơng
Nếu sử dụng tên biến đó trong chơng trình chính thì đơng nhiên sẽ hiểu là tên
biến toàn cục, vì chơng trình chính không hiểu các biến cục bộ.
4. Phân biệt giữa tham trị và tham biến :
Tham biến (variable parameter)
- Có từ khoá VAR đi trớc.
- Tham số thực tơng ứng phải là một biến cùng kiểu, không thể là một hằng hoặc
biểu thức.
- Mọi thay đổi về mặt giá trị của các tham số dạng tham biến trong chơng trình
con đều làm thay đổi giá trị của biến thực tơng ứng của chơng trình gọi nó.
Tham số trị (value parameter)
- Không có từ khoá VAR đi trớc.
- Tham số thực tơng ứng có thể là một biến, hằng hoặc biểu thức.
- Mọi thay đổi về mặt giá trị của các tham số dạng tham trị trong chơng trình
con đều không làm thay đổi giá trị của biến thực tơng ứng của chơng trình gọi
nó.
Ví dụ 7 : Hai chơng trình dới đây sẽ chỉ rõ sự khác nhau giữa tham biến và
tham trị trong các chơng trình con.
Chơng trình 1.
Program Tham_tri;
Var a,b,c : Integer;
Procedure InTri(x,y,z : Integer);
Begin
x:=x*2; y:=y*2; z:=z*2;
Writeln('Trong thủ tục ta có 3 số : ',x, #32, y, #32, z);
End;
Begin
a:=10; b:=20; c:=30;
Writeln('Trớc khi vào thủ tục ta có 3 số : ',a, #32, b, #32, c); {10 20 30}
Intri(a,b,c); {20 40 60}
Writeln('Sau khi ra thủ tục ta có 3 số : ',a, #32, b, #32, c); {10 20 30}
End.
Trong thí dụ này ta thấy a,b,c không thay đổi mặc dầu Thủ tục InTri đã nhân x,
y, z cho 2.
Chơng trình 2.
Program Tham_bien;
Var a,b,c: Integer;
Procedure InTri(Var x,y,z : Integer);
Begin
x:=x*2; y:=y*2; z:=z*2;
Writeln('Trong thủ tục ta có 3 số : ',x,#32,y,#32,z);
End;
Begin
72
Phần 3- Ngôn ngữ lập trình Pascal
a:=10; b:=20; c:=30;
Writeln('Trớc khi vào thủ tục ta có 3 số : ',a, #32,b,#32,c); {10 20 30}
Intri(a,b,c); {20 40 60}
Writeln('Sau khi ra thủ tục ta có 3 số : ',a, #32,b,#32,c); {20 40 60}
End.
Trong thí dụ này, khi thủ tục InTri nhân x, y, z với 2 thì 3 biến toàn cục a, b, c
bị thay đổi.
12.5. Cách dùng Hàm/Thủ tục : Sau đây là một số gợi ý :
Dùng Function Dùng Procedure
1. Đối với các chơng trình con có tính
chất giống hàm toán học và trả về
một giá trị duy nhất thuộc kiểu kiểu
vô hớng (nguyên, thực, logic, char)
hoặc kiểu con trỏ.
2. Đối với các chơng trình con mà lời
gọi nó cần xuất hiện trong biểu thức.
Chẳng hạn các hàm toán học : giai
thừa, tổ hợp, chỉnh hợp ...
1. Đối với chơng trình con có tính chất
tính toán nhng trả về cùng lúc nhiều giá
trị (nh thủ tục giải phơng trình bậc
hai ...).
2. Đối với các chơng trình con thực hiện
một hành động nào đó, không trả lại
giá trị nào.
Ví dụ 8 : Nhập vào từ bàn phím một dãy các số nguyên rồi xuất ra màn hình
phần tử lớn nhất của dãy.
(Yêu cầu dùng hai chơng trình con : một chơng trình con nhập dãy và một chơng
trình con tìm phần tử lớn nhất trong dãy).
Program Vidu_8;
Uses Crt;
Type Day = array[1..20] of Integer;
Var N : Integer;
a : Day;
Procedure NHAP(Var x : Day; N : Integer; ten : Char);
Var i : Integer;
Begin
For i:=1 to N do
Begin
Write(ten,'[',i,'] = ');Readln(x[i]);
End;
End;
Function MAX(x : Day; N : Integer) : Integer;
Var i, z : Integer; {z : phan tu lon nhat}
Begin
z := x[1];
For i:=2 to N do
73
Giáo trình tin học đại cơng
if x[i] > z then z := x[i];
max := z;
End;
Begin {chuong trinh chinh}
Repeat
ClrScr;
Write('Nhap so phan tu N = '); Readln(N);
Until (N>0) and (N<21);
NHAP(a,N,'a');
Writeln('Phan tu lon nhat la : ',MAX(a,N));
Readln
End.
12.6. Một số hàm và thủ tục của TURBO PASCAL
12.6.1. Các thủ tục :
CLRSCR : Xoá toàn bộ màn hình và đa cursor về vị trí (1,1) trên màn hình.
CLREOL : Xoá từ cursor đến cuối dòng hiện hành.
INSLINE : Chèn dòng trống vào vị trí hiện hành của cursor trên màn hình.
DELLINE : Xoá dòng cursor đang đứng, các dòng sau sẽ đợc chuyển lên
trên một dòng.
TEXTMODE(Mode:Integer) : Chọn chế độ trình bày văn bản. Tham số
Mode sẽ lấy giá trị :
- BW40 = 0; {40x25 B/W trên Color Adapter}
- CO40 = 1; {40x25 Color trên Color Adapter}
- BW80 = 2; {80x25 B/W trên Color Adaptor}
- CO80 = 3; {80x25 Color trên Color Adapter}
- Mono = 7; {80x25 trên Monochrome Adaptor} Font8x8 = 256; EGA và
VGA43, 50 line mode
- LastMode=-1; {Khi khai TextMode(LastMode) Chơng trình tự động text xem
mode màn hình trớc đó là gì và lấy mode đó khai cho Chơng trình. Điều này
đặc biệt thuận lợi khi ta muốn Chơng trình chạy đợc trên nhiều loại máy khác
nhau}.
Ví dụ 9 :
Program Vidu_9;
Uses Crt;
Var OrigMode : Integer;
Begin
OrigMode:=LastMode; {Lu mode màn hình hiện hành vào biến OrigMode}
TextMode(CO40);
Writeln('Big Characters');
74
Phần 3- Ngôn ngữ lập trình Pascal
Readln;
TextMode(OrigMode);
Writeln('Normal Characters');
Readln;
End.
WINDOW(x1,y1,x2,y2 : Byte) : Thiết lập một cửa sổ để trình bày. (x1,y1) là
toạ độ góc trái phía trên của cửa sổ, (x2, y2) là toạ độ góc phải phía dới của
cửa sổ. Cửa sổ mặc nhiên là Window(1,1,80,25). Thủ tục này ảnh hởng đến
các lệnh trình bày dữ liệu trên màn hình ở chế độ văn bản.
GOTOXY(x,y : Byte) : Đa cursor đến toạ độ (x,y) : dòng y, cột x.
HIGHVIDEO : Cho phép hiển thị trên màn hình với độ chói cao.
LOWVIDEO : Cho phép hiển thị trên màn hình với độ chói thấp.
NORMVIDEO : Khôi phục thuộc tính màn hình mặc định theo các thuộc
tính đã hiện hữu ở vị trí cursor khi thực hiện chơng trình.
SOUND(Hz : Word) : Phát âm thanh có tần số Hz cho đến khi gặp thủ tục
Nosound
NOSOUND : Tắt loa phát âm thanh ở máy.
DELAY(ms:Word) : Tạm treo chơng trình trong thời gian ms mili giây.
TEXTCOLOR(Color : Byte) : Chọn màu của kí tự trình bày trên màn hình.
Color có giá trị từ 0 ữ 255.
TEXTBACKGROUND(Color:Byte) : Chọn màu nền trong chế độ văn bản.
Các hằng xác định màu nền và chữ :
Black = 0 Brown = 6 LightRed = 12
Blue = 1 LightGray = 7 LightMagenta = 13
Green = 2 DarkGray = 8 Yellow = 14
Cyan = 3 LightBlue = 9 White = 15
Red = 4 LightGreen = 10
Magenta = 5 LightCyan = 11
Hằng xác định nhấp nháy: Blink = 128
Chẳng hạn, TextColor(15) hay TextColor(White) là chọn chữ màu trắng.
Ví dụ 10 :
Program Vidu_10;
Uses crt;
Begin
TextBackGround(Blue);
Textcolor(White + Blink);
Clrscr;
GotoXY(20,10);
Write('Trung tâm Tin học Hớng Dơng'); {chữ trắng trên nền xanh có chớp nháy }
75
Giáo trình tin học đại cơng
Readln
End.
Chú ý : Nếu muốn màu nền loang đều trên màn hình thì sau khi phát lệnh
TextBackGround(Color) thì đánh tiếp lệnh Clrscr;
12.6.2. Các hàm :
KEYPRESSED : Kiểm tra xem có nút nào đợc nhấn trên bàn phím không.
Nếu có cho giá trị true, nếu không cho giá trị False
READKEY : Đọc kí tự từ bàn phím (kí tự nhập không đợc trình bày trên màn
hình).
Các phím trên bàn phím nh : A, B, C, ... chỉ tạo một kí tự khi đợc nhấn. Còn
các phím chức năng F1, F2,..., Home, End, Alt ...,Ctrl ... tạo hai kí tự khi đợc
nhấn, trong đó kí tự thứ nhất sẽ mang mã 0. Hàm Readkey chỉ đọc đợc kí tự thứ
nhất (kết quả đều nh nhau). Để sử dụng đợc các phím này trong lập trình ta sử
dụng cách sau :
Ch:=Readkey;
If Ch=#0 then Ch:=Readkey;
Nếu nhấn nút chức năng thì đọc tiếp kí tự còn lại trong bộ đệm của
Keyboard.
Ví dụ 11 :
Program Vidu_11;
Uses Crt;
Var Ch : Char;
Begin
Writeln('Nhan 1 phim: '); Ch:=Readkey;
If Ch=#0 Then
Begin
Ch:=Readkey;
Writeln('Bạn vừa nhấn phím đặc biệt có mã : ', Ord(Ch));
End
Else Writeln('Bạn vừa nhấn phím có mã ASCII là : ', Ord(Ch));
Readln;
End.
WHEREX : Cho giá trị kiểu byte cho biết cursor đang ở cột nào.
WHEREY : Cho giá trị kiểu byte cho biết cursor đang ở dòng nào.
Ví dụ 12 :
Program Vidu_12;
Uses Crt;
Var X,Y : Integer; Ch : Char;
Procedure Tieu_de;
Begin
76
Phần 3- Ngôn ngữ lập trình Pascal
Clrscr;
Writeln(' CHUONG TRINH THUC HIEN CAC PHEP TOAN SO HOC ');
Writeln(' ------------------------------------------------------------------------------');
End;
Function Cong(a,b : Integer):Integer;
Begin
Cong:=a+b;
End;
Function Tru(a,b : Integer):Integer;
Begin
Tru:=a-b;
End;
Function Nhan(a,b: Integer):Integer;
Begin
Nhan := a*b;
End;
Function Chia(a,b : Integer):Real;
Begin
Chia:=a/b;
End;
Function Luythua(a : Integer; n : Byte):Integer;
Var Tempo : real;
Begin
If n=0 Then Luythua := 1
Else If n>0 Then Luythua:= a*Luythua(a,n-1);
End;
BEGIN { Chuong trinh chinh }
Tieu_de;
Writeln('Nhap 2 so nguyen: ');
Write('X = '); Readln(X); Write('Y = '); Readln(Y);
Write('Chon phep tinh ( + - * / ^): ');
Readln(Ch);
Case Ch of
'+': Write('Ket qua: X',Ch,'Y = ',Cong(X,Y));
'-': Write('Ket qua: X',Ch,'Y = ',Tru(X,Y));
'*': Write('Ket qua: X',Ch,'Y = ',Nhan(X,Y));
'/': Write('Ket qua: X',Ch,'Y = ',Chia(X,Y):1:2);
'^': Write('Ket qua: X',Ch,'Y = ',Luythua(X,Y));
End;
Readln;
END.
12.7. Thủ tục và hàm đệ qui (recursion) :
Trong Pascal chuẩn cũng nh Turbo Pascal đều cho phép gọi đệ qui trong
các chơng trình con thủ tục và hàm. Một thủ tục hay hàm là đệ qui nếu trong nó
chứa lời gọi đến chính nó. Để thấy rõ điều này, ta xét ví dụ sau :
77
Giáo trình tin học đại cơng
Ví dụ 13 : Viết chơng trình nhập N từ bàn phím, tính và in ra giá trị N!. Trong
chơng trình có xây dựng và sử dụng một hàm đệ qui tính k!.
Lu ý :
>
=
=
.0)!1(
01
!
kkhik
kkhi
k
Muốn tính k! ta phải tính (k-1)!, muốn tính (k-1)! lại phải tính (k-2)!,...,
suy ra cuối cùng phải tính đợc 0!, nhng vì 0!=1 nên quá trình kết thúc.
Program Vidu_13;
Uses Crt;
Var N : Integer;
Function GT(k:Integer): LongInt; {Hàm tính giai thừa bằng đệ qui}
Begin
If k=0 then GT:=1
else GT:=GT(k-1)*k;
End;
Begin
Repeat
ClrScr;
Write('Nhap N = ');
Readln(N);
Until N >= 0;
Writeln(N,'! = ',GT(N));
Readln;
End.
Chú ý : Việc sử dụng đệ qui đòi hỏi nhiều về bộ nhớ, do đó nên tránh dụng đệ
qui nếu có thể đợc.
Bài tập
[12.1] Viết chơng trình tính tổng:
1! + 2! + 3! + ... + n! với n<=12 đợc nhập từ bàn phím.
[12.2] Viết chơng trình có các chức năng sau :
a. Tìm ớc số chung lớn nhất của 2 số nguyên
78