Trong bất kỳ môi trường lập trình nào thì cũng có những trường hợp ứng dụng nhất định mà chúng ta cần phải định nghĩa một cấu trúc dữ liệu Đệ Quy. Tức là trong code định nghĩa đang viết có sử dụng ngay tên của chính kiểu dữ liệu đó. Và ở đây chúng ta sẽ tìm hiểu về thao tác định nghĩa một cấu trúc như vậy trong Ada
.
Recursive Record
Trong môi trường của C
như đã biết - khi viết định nghĩa struct
, chúng ta có thể sử dụng ngay tên của kiểu struct
đang định nghĩa để định kiểu cho các trường dữ liệu bên trong nếu cần thiết. Tuy nhiên thông thường thì khi làm việc với các struct
chúng ta sẽ sử dụng định kiểu dạng con trỏ và như vậy một struct
đệ quy thường có dạng như thế này:
typedef struct node { void* data; struct node* next;
} node_struct;
Và ở đây trong Ada
, để định nghĩa một record
đệ quy thì chúng ta cũng có thể sử dụng ký thuật tương tự với sự hỗ trợ của khái niệm con trỏ access
.
package Item is type Struct; -- early declaration type Pointer is access all Struct; type Struct is record Value: Integer; Next: Pointer; end record; private -- nothing here end Item;
Và khi sử dụng kiểu con trỏ access
để làm tên định kiểu chính cho record
giống như khi code C
thì chúng ta có sẵn trình khởi tạo với cú pháp new
vay mượn từ nhóm công cụ Lập Trình Hướng Đối Tượng Object-Oriented Programming
. Cú pháp này sẽ tách lấy địa chỉ tham chiếu để trả về cho biến access
lưu lại.
with Ada.Text_IO; use Ada.Text_IO;
with Item; use Item; procedure Main is Instance, Cursor : Item.Pointer;
begin Instance := new Item.Struct'(0, null); Instance := new Item.Struct'(1, Instance); Instance := new Item.Struct'(2, Instance); Instance := new Item.Struct'(3, Instance); Instance := new Item.Struct'(4, Instance); Instance := new Item.Struct'(5, Instance); Instance := new Item.Struct'(6, Instance); Instance := new Item.Struct'(7, Instance); Instance := new Item.Struct'(8, Instance); Instance := new Item.Struct'(9, Instance); -- move cursor from the first item and print the list Cursor := Instance; Put ("List : "); while Cursor /= null loop Put (Integer'Image (Cursor.Value) & " "); Cursor := Cursor.Next; end loop;
end Main;
List : 9 8 7 6 5 4 3 2 1 0
Dereference Access
Trong những trường hợp nhất định khi làm việc với các kiểu cấu trúc khác - không phải là mảng array
hoặc record
- thì chúng ta sẽ phải thực hiện thao tác chuyển kiểu trong code. Thao tác truy xuất tổng bộ nội dung của một access
và gán trả về một biến thuộc kiểu dữ liệu nguyên bản được gọi là dereference
và được thực hiện thông qua khóa all
của con trỏ access
như sau:
Pointer : Item_Access := new Item'( Value => 1, Next_Access => null );
Origin : Item := Pointer.all;
Ở chiều ngược lại, khi chúng ta muốn tách lấy địa chỉ từ một biến thông thường lưu trữ array
hoặc record
để gán vào một biến con trỏ access
thì chúng ta cần đảm bảo các điều kiện là:
- Phải tạo một biến cục bộ
local
củasub-program
được khai báo với từ khóaaliased
ở phía trước tên định kiểu. - Biến con trỏ
access
cũng phải được khai báo cục bộ đối vớisub-program
.
Các điều kiện này là để đảm bảo rằng, thao tác chuyển kiểu từ giá trị Value
sang kiểu địa chỉ Access
cần được kiểm soát trong phạm vi cục bộ của từng sub-program
. Đây là một dạng thiết kế an toàn mà Ada
tạo ra để đảm bảo việc sử dụng kiến trúc con trỏ access
phải được kiểm soát chặt chẽ trong logic nội tại của người viết code.
Sau đó thao tác tách lấy địa chỉ tham chiếu và gán vào biến con trỏ access
sẽ có thể thực hiện được nhờ thuộc tính Value'Access
.
Value : aliased Item;
Pointer : Item_Access;
Pointer := Value'Access;
Mặc dù vẫn còn rất nhiều thứ để nói về record
trong Ada
tuy nhiên chúng ta sẽ để dành cho một Sub-Series khác nói về tư duy Lập Trình Hướng Đối Tượng. Ở đây chúng ta sẽ chuyển sang tìm hiểu những công cụ phổ biến khác mà Ada
cung cấp cho môi trường lập trình phổ thông (không bao gồm embedded
).
[Procedural Programming + Ada] Bài 10 - Overloading Sub-program & Generics