Phân trình và tham số
Việc truyền tham số cho
phân trình (subprogram) trong những ngôn ngữ khác nhau được giải quyết khác nhau. Java luôn truyền các kiểu đơn giản bằng giá trị và truyền các đối tượng bằng tham chiếu. C# còn cho phép cả truyền kiểu đơn giản bằng tham chiếu và ngược lại, cho phép đổi giá trị của tham chiếu truyền thành đối tượng trong thân hàm. C++ cho phép làm mọi chuyện có lý và vô lý, thậm chí truyền cả đối tượng bằng giá trị.
Nhưng bản chất ở những ngôn ngữ đó thì luôn giống nhau: người lập trình bằng cách này hay cách khác chỉ rõ phương pháp truyền tham số. Anh ta phải tự quyết định một trong nhiều phương án, nếu có, để dung hòa giữa tính năng và hiệu quả. Ở Ada, các tác giả của ngôn ngữ đã đi theo con đường riêng, độc đáo của mình. Việc truyền tham số bằng giá trị hay tham chiếu, người lập trình không quyết định mà để cho bộ biên dịch. Người lập trình chỉ ấn định chiều vào / ra của dòng thông tin đi qua tham số. Để phân biệt hai chiều đi, Ada dùng hai từ khóa
in và
out. Từ đó ta phân biệt ba loại tham số:
in chứa thông tin chỉ đi vào phân trình,
out chứa thông tin chỉ đi ra khỏi phân trình, và
in out chứa cả hai loại thông tin. Sau đây là vài thí dụ.
Code: type Index is new Integer;
type Value is new String(1..10);
type Table is array(Index) of Value;
function Middle (X, Y: in Index) return Index is
begin
return (X + Y)/2;
end;
procedure Increment(X: in out Index) is
begin
X := X+1;
end;
procedure Decrement(X: in out Index) is
begin
X := X-1;
end;
procedure Exchange (A, B : in out Value) is
C: Value;
begin
C:=A; A:=B; B:=C;
end;
procedure QuickSort(A: in out Table) is
procedure Sort(l, h: Index) is
i, j : Index;
x : Value;
begin
i := l;
j := h;
x := A(Middle(l,h));
loop
while A(i) < x loop Increment(i); end loop;
while x < A(j) loop Decrement(j); end loop;
if i <= j then
Exchange(A(i),A(j));
Increment(i);
Decrement(j);
end if;
exit when i > j; -- lệnh exit thoát khỏi vòng lặp
end loop;
if l < j then Sort(l, j); end if;
if i < h then Sort(i, h); end if;
end Sort;
begin -- QuickSort
if A'Length > 1 then
Sort(A'First, A'Last);
end if;
end QuickSort;
Hàm Middle(X, Y) tính số nguyên nằm ở chính giữa hai số X, Y. Thông tin X, Y rõ ràng là chỉ đi vào trong hàm. Vậy cả hai tham số đều là tham số
in. (11) Điều đáng lưu ý là hai tham số
in trong thân phân trình được xem như hằng, do đó bộ biên dịch sẽ báo lỗi khi ta mưu toan thay đổi giá trị của X hay Y. Một điều đáng chú ý nữa là khác với thủ tục, hàm chỉ được phép nhận tham số
in. Vậy các hàm Ada hành xử giống hệt như các hàm toán học (12), nghĩa là từ một hay nhiều tham số, tính ra một kết quả. Do Integer là một kiểu đơn giản, chắc là nó sẽ được truyền bằng giá trị. Nhưng nếu tham số là một mảng, chắc là bộ biên dịch sẽ quyết định truyền bằng tham chiếu.
Tham số vào (
in và
in out) phải có giá trị xác định khi gọi phân trình, vì thế lời gọi phân trình khi các tham số vào của nó chưa được gán trị là lỗi. Tương tự, tham số ra (
out và
in out) cũng như kết quả của hàm phải có giá trị xác định khi thoát khỏi phân trình quay trở về điểm gọi, vì thế mưu toan trả điều khiển về điểm gọi khi chúng chưa được gán trị là lỗi. Bộ biên dịch có thể phát hiện các lỗi này và cảnh báo, hoặc nếu không phát hiện được thì cũng sinh mã đích gây ngoại lệ
Program_Error.
Thủ tục Increment(X) cộng thêm cho X một đơn vị. Thủ tục Decrement(X) trừ bớt của X một đơn vị. Thủ tục Exchange(A, B) hoán trị hai biến A, B. Tất cả đều là tham số
in out nhưng X có lẽ sẽ được truyền bằng giá trị còn A, B có lẽ được truyền bằng tham chiếu – quyết định là việc của bộ biên dịch.
Đoạn mã nguồn còn lại – QuickSort – sử dụng các phân trình trên. Tác dụng của nó thế nào, chắc chả cần phải trình bày nữa.
________________________
NOTES
(11) Từ khóa
in là không bắt buộc đối với tham số
in, xem thủ tục Sort.
(12) Một phân trình không có hiệu ứng phụ, nghĩa là chỉ trao đổi thông tin với bên ngoài qua tham số và kết quả được gọi là phân trình
thuần túy (pure). Cho nên nếu muốn thật đúng thì phải nói: trong Ada, hàm thuần túy hành xử giống như hàm toán học.