1. KHÁI NIỆM VỂ CHƯƠNG TRÌNH CON : [SUB PROGRAM]
Trong khi lập chương trình, chúng ta thường gặp những đoạn chương trình được lặp đi lặp lại nhiều lần ở nhũng chỗ khác nhau. Để tránh rườm rà, những đoạn chương trình này được thay thế bằng những chương trình con tương ứng và khi cần, ta chỉ việc làm thủ tục gọi chương trình đó ra (với các tham số tương ứng cần thiết) mà không phải viết lại cả đoạn chương trình đó. Thí dụ, khi làm các bài toán tính toán, ta hay phải tính giá trị Max hoặc Min của 2 số nào đó. Như vậy ta cần lập một chương trình con có tên là Max (hoặc Min) và tham số cần thiết là a, b. Những chương trình con thông dụng đã được xây dựng sẵn để trong thư viện chương trình con mẫu” và được chương trình dịch PASCAL quản lí, vì vậy còn được gọi là các chương trình con chuẩn. Trong TURBO PASCAL, các chương trình UNIT như CRT, PRINTER, DOS...
Một lí do khác để lập chương trình con là : một vấn đề lớn và phức tạp tương ứng với một chương trình có thế rất lớn, rất dài. Do đó việc nhìn tổng quan cả chương trình cũng như việc gỡ rối, hiệu chỉnh sẽ rất khó khăn. Ta có thể phân tích vấn đề phức tạp đó ra thành các vấn đề nhỏ hơn (tương ứng với các chương trình con, những modul) để dễ kiểm tra, gỡ rối từng khối một và sau đó ghép lại thành chương trình lớn. Việc này tương ứng trong dây chuyền sản xuất công nghiệp, người ta có thế lắp ráp sản phẩm từ các bán thành phẩm, từ các modul đã được chế tạo sẵn từ nơi khác chuyển đến. Đó cũng là ý tưởng cơ bản của khái niệm lập chương trình có cấu trúc. Cần lưu ý là chương trình con này có khi chỉ được dùng đúng một lần, song nó có tác dụng làm sáng sủa vấn đề trong khi lập chương trình. Việc chia nhỏ chương trình thành các modul có thể ví như các nguyên tắc “Chia để trị, chia để dễ điều khiển”.
Chương trình con được dùng rất phố biến. Trong lập trình hiện đại, không thể nào bỏ qua nó. Vì vậy chúng ta càn nắm vững các kĩ thuật lập chương trình con.
2. THỦ TỤC VÀ HÀM [PROCEDURE VÀ FUNCTION]
Trong Pascal có hai loại chương trình con (CTC) : Thủ tục (Procedure) và hàm (Function). Hàm trả lại cho một kết quá vô hướng thông qua tên của hàm và do dó được phép sử dụng nó trong một biểu thức. Còn Procedure không trả lại kết quả thông qua tên của nó nên các Procedure không thể viết trong các biểu thức. Ví dụ chương trình con tính sin(x) thuộc loại Function, có tên là sin với tham số là x, còn lệnh Writein, Readln mà ta đã học là các thủ tục.
Dưới đây, CTC được hiểu bao hàm cả Procedure và Function. Theo qui định của Pascal chuẩn, cấu trúc chung của một chương trình với thứ tự mô tả khai báo như sau :
PROGRAM TEN_CHUONG_TRINH ;
LABEL {Khai báo các nhãn} ;
…….
CONST {Khai báo các hằng} ;
TYPE {Mô tả và định nghĩa các kiểu cấu trúc dư liệu mới của người sử dụng} ;
…….
VAR {Khai báo các biến} ;
{…… Các chương trình con …….}
PROCEDURE TEN-THU-TUC (Khai báo các tham số, nếu cần) ;
{Khai báo Label, Const, Type, Var của riêng Procedure nếu cần}
……
Begin
…….. {Thân chương trình con}
End ;
{…………………….}
1 1
r
FUNCTION TEN_HAM (Khai báo các tham số, nếu cần) : kiểu dữ liệu ;
{Khai báo Label, Const, Type, Var của riêng Procedure nếu cần}
……
Begin
….. {Thân chương trình con}
End ;
{…. Thân chương trình chính ……}
BEGIN (Bắt dầu chương trình chính}
……
END
Cũng theo qui định này, thứ tự các phần mô tả và khai báo phải theo đúng trật cự trêu, nghĩa là Label trước hết, rồi đến Const, Type, Var (và mỗi oại chỉ được xuất hiện một lần) ; cuối cùng là các Procedure, các Function. Phần nào không có thì bỏ đi, đương nhiên không thể thiếu phần thân chương trình chính. TURBO PASCAL còn cho phép phần khai báo được rộng rãi hơn, không nhất thiết theo thứ tự bất buộc trên và không nhất thiết khai báo các phần một lần.
Để dễ dàng minh họa chúng ta xét một chương trình sau :
PROGRAM VI_ DU ;
USES CRT ;
VAR
A, B, C, D : INTEGER ;
Z : REAL;
{……………………………………….}
PROCEDURE TIEU_DE ;
Begin
Writeln ('*************************');
Writeln (' * MINH HOA CHUONG TRINH CON * ') ;
Writeln ('*************************’);
End ;
{……………………………………….}
PROCEDURE ENTER (VAR X, Y : INTEGER)
VAR OK : char ;
Begin
REPEAT
Write ( Tu_so =') ; Readln(X) ;
Write ('Mau_so =') ; Readln(Y);
Write ('Co sua so lieu khong (co/khong)' ?');
OK := Readkey ;
Writeln ;
UNTIL (OK = 'K') or (OK = 'k');
End ;
{……………………………………….}
FUNCTION CHIA (X, Y : INTEGER): REAL ;
Begin
If Y <> 0 then CHIA := X/Y
Else
Begin
Writeln (#7, 'Không chia duoc vi mau so = 0.');
Halt; (* thủ tục Halt dừng chương trình lại. *)
End ;
End ;
{……………………………………….}
Begin
Tieu_de ;
Enter(A, B);
Enter(C, D) ;
Z := CHIA(A, B) * CHIA(C, D);
Writeln (Ti so (A/B) * (C/D) la:’, Z);
Write ('Hay an Enter de tiep tuc !'); Readln ;
End.
Chương trình này sẽ đọc từng cặp số nguyên A, B và C, D, sau đó tính tỉ số hai số đó qua function CHIA, cuối cùng tính tích của hai tỉ số đó rồi báo ra kết quả. Như vậy nhìn vào thân chương trình chính ta thấy công việc được hình dung ra một cách rất sáng sủa.
Chúng ta hãy lần lượt xét cụ thể từng CTC.
Đầu tiên CTC Tieu_de có nhiệm vụ in ra vài dòng tiêu đề. CTC này không cần tham số.
Procedure Enter có nhiệm vụ là vào dữ liệu X, Y. X và Y được gọi là hai tham số hình thức. X và Y cũng đồng thời là kết quả của Procedure. Hai tham số này sẽ được thay thế lần lượt bằng hai tham số thực sự là C và D trong lần thứ 2 gọi đến thủ tục Enter tức là lần lượt nhập vào giá trị của 2 cặp số A, B và C, D.
Trong CTC ta cũng có thể có phần khai báo riêng của nó. Trong ví dụ trên, biến OK là biến riêng hay còn thường được gọi là biến cục bộ (local variable) của Procedure Enter để phân biệt với các biến toàn cục A, B, Z (global variable). Biến OK này chỉ có tác dụng hoạt động ở trong phạm vi Procedure Enter, nghĩa là ta không thế dùng nó (như là gán hay đọc giá trị) ở ngoài thụ tục Enter. Một qui tắc chung là khai báo trong các CTC tất cả các biến không cần dùng đến ở trong chương trình chính.
Function CHIA có hai tham số hình thức X và Y. Ta có thể viết lời gọi function trong biểu thức như việc tính Z ở ví dụ trên. Lời gọi Procedure không làm như vậy được vì tên của không có giá trị. Khi khai báo Procedure ta còn phải khai báo thêm kiểu dữ liệu của function, trong ví dụ trên function CHIA có kiểu Real. Đó cũng là sự khác nhau cơ bản giữa hai loại CTC.
Giả sử ta tính Z = (A/B)(C/D), ta có thể viết : Z := CHIA (Chia (A, B), (C, D)) ;
Việc khai báo hai tham số X và Y trong phần khai báo Procedure Enter được đặt sau chữ Var nói lên rằng hai tham số này được chuyến cho CTC dưới dạng tham số biến (variable parameter) hay ngắn gọn hơn là tham biến nghĩa là ta có quyền thay đổi giá trị của hai biến X và Y ở trong CTC (đó là lệnh đọc giá trị X và Y trong ví dụ trên) và khi ra khỏi CTC, giá trị mà các tham số thực (A, B hay C, D mang chính là giá trị của các tham biến X và Y trước khi thoát khỏi thủ tục. Còn trong phần khai báo function CHIA hai tham số X và Y không được đặt sau chữ Var, điều đó có nghĩa là hai tham số X và Y được chuyển cho CTC dưới dạng tham số giá trị hay còn được gọi ngắn gọn hơn là tham trị (value parameter). Khi đó CTC chỉ được phép dùng giá trị của tham trị. Nếu chúng ta thay đổi giá trị của nó, giá trị của các tham số thực cũng không bị ảnh hưởng. Điều đó có nghĩa là giá trị của tham số thực A trước và sau khi gọi CTC đều như nhau, không có sự thay đổi nào do CTC đem lại. Chúng ta sẽ còn trở lại vấn đề thông qua các ví dụ minh họa tiếp dưới đây :
Dòng lệnh cuối cùng trong chương trình trên là một cách để dừng chương trình lại cho bạn quan sát, cho đến khi bạn ấn phím Enter thì mới tiếp tục.
Cấu trúc chung của Procedure và Function là như sau :
PROCEDURE TEN_THU_TUC (Khai báo các tham số hình thức) ;
{Khai báo Label, Const, Typr, Var và thậm chí cả các Procedure và Function}
…….
Begin
...{thân chương trình con}
End ;
và
FUNCTION TEN, HAM (Khai báo các tham số hình thức) : kiểu dữ liệu của hàm ;
(Khai báo Label, Const, Typr, Var và thậm chí cả các Procedure và Function)
…..
Begin
...{thân chương trình con}
End ;
và
FUNCTION TEN-HAM (Khai báo các tham số hình thức) : kiểu dữ liệu của hàm ;
{Khai báo Label, Const, Typr, Var và thậm chí cả các Procedure và Function }
…….
Begin
...{thân chương trình con}
End ;
Thân của CTC được đặt giữa hai chữ Begin và End, với End kết thúc bằng dấu chấm phẩy (;) chứ không phải là dấu chấm (.) như của chương trình chính.
Khối (Block) chương trình : được định nghĩa là phần chương trình bao gồm phần khai báo và phần lệnh. Như vậy trong chương trình có 3 loại block :
- Block chính là thân chương trình chính, kết thúc bằng END.
- Block của Procedure.
- Block của Function.
Hai loại sau kết thúc bằng chữ END và dấu chấm phẩy (;).
Một chương trình có thể khoanh lại thành nhiều block.
Dưới đây là chương trình giải phương trình bậc hai được viết lại thành chương trình con với minh họa cấu trúc block.
PROGRAM VI_DU_GIAI_PT_BAC_2 ;
VAR
M, N, K : Real ;
Procedure GIAI_PT_BAC_HAI (A, B, C : Real) ;
Var
Delta, x1, x2 : Real ;
Procedure DeltaAm ;
Begin
Writeln ('PT vô nghiệm !') ;
End ;
Procedure Deltakhong (x : Real) ;
Begin
Writeln ('PT có 1 nghiệm kép :’.x) ;
End ;
Procedure DeltaDuong (x1, x2 : Real) ;
Begin
Writeln ('PT có 2 nghiệm : x1=’,x1, x2= 'x2=', x2) ,
End ;
Begin
Delta := b * b - 4 * a * c ;
iF Delta < 0 then DeltaAm
else if Delta = 0 then
begin
x1 := -b/(2 * a);
Deltakhong (x1);
end ;
else
begin
x1 := (-b+Sqrt(Delta))/(2 * a) ;
x2 := (-b-Sqrt(Delta))/(2 * a) ;
DeltaDuong(x1, x2) ;
end ;
End ;
BEGIN
Writeln ('Nhập 3 hệ số;') ;
Readln(M, N, K);
if (M = 0) then writeln (’không phải PT bậc 2 !') ;
else GIAI_PT_BAC_HAI(M, N, K);
END.
Thí dụ này cho thấy trong thủ tục GIAI_PT_BAC_HAI có khai báo biến địa phương riêng của nó là Delta và có 3 thủ tục nhỏ hơn tương ứng với 3 trường hợp của Delta. Còn chương trình chính chỉ việc đọc các hệ số K, M. N tương ứng với các hệ số A, B, C của phương trình bậc hai và sau đó chúng được đưa vào lời gọi thủ tục GIAI_PT_BAC_HAI.