Lập trình theo thủ tục + Ada, Bài 4 – Gói & Chương trình con

Tram Ho

Trong tất cả các ví dụ trước đó thì chúng ta đã sử dụng một tệp main.adb duy nhất được chỉnh sửa code nhiều lần để tìm hiểu về các yếu tố đơn giản. Tuy nhiên, Ada rất xem trọng việc tách chương trình mà chúng ta viết thành nhiều tệp khác nhau và vì vậy nên chỉ cho phép định nghĩa một sub-program duy nhất trong mỗi tệp.

Dòng warning đầu tiên là do mình đã khai báo thêm có sử dụng C kèm trong project, nếu bạn không muốn thấy thông báo này hiện lặp lại thì có thể bỏ C trong danh sách ngôn ngữ sử dụng trong tệp learn_ada.gpr.

Như vậy là trình biên dịch sẽ báo lỗi ngay ở vị trí mà chúng ta bắt đầu định nghĩa khối procedure thứ hai. Và để tiếp tục tiến xa hơn, chúng ta sẽ cần phải tìm hiểu cách tạo ra và sử dụng các package.

Package

Khái niệm package trong Ada có thể được hiểu với ý nghĩa tương đương với thư viện library trong C. Chúng ta sẽ cần tạo ra một tệp khai báo tổng quan bao gồm các hằng số constant và chữ ký của các sub-program mà thư viện đó cung cấp. Tương ứng với kiểu tệp khai báo header.h của C thì chúng ta sẽ có một tệp cấu hình specification.ads. Và tệp mã nguồn cũng chứa code logic chi tiết được Ada gọi là body.adb và yêu cầu đặt cùng tên với tệp cấu hình.

Đây là một ví dụ về code khai báo cấu hình và viết code định nghĩa chi tiết cho một package:

Bây giờ chúng ta đã có thể sử dụng package Week trong chương trình chính.

Trong trường hợp cần kiến trúc chương trình với nhiều package xếp lớp. Ví dụ như trong Week chúng ta cần chia nhỏ thành các package nhỏ hơn thì chúng ta có thể thực hiện như sau. Trong thư mục week chúng ta sẽ tạo thêm thư mục child.

Và sử dụng Week.Child tại chương trình main giống như một package độc lập.

Sub-program

Có hai kiểu sub-program trong Ada, đó là các các Thủ Tục procedure và Hàm function. Điểm khác biệt căn bản giữa hai kiểu sub-program này đó là một Hàm function sẽ luôn luôn trả về một giá trị ở vị trí được gọi, còn một Thủ Tục procedure thì không bao giờ. Và ngược lại, một Hàm function sẽ không bao giờ tạo ra thay đổi tác động tới các yếu tố bên ngoài chính nó, còn một Thủ Tục procedure lại thường được sử dụng để tác động lên các yếu tố bên ngoài ví dụ như trạng thái hiển thị và các biến ngoại vi.

Hay nói một cách khác, Ada không hỗ trợ việc tạo ra một sub-program vừa tạo ra sự thay đổi tới môi trường bên ngoài và vừa trả về một giá trị ở vị trí được gọi. Và chính sự phân bổ chức năng của các sub-program được định hình rõ ràng ở cấp độ cú pháp của ngôn ngữ như thế này, sẽ buộc chúng ta hình thành thói quen thiết kế các sub-program có chủ đích rõ ràng hơn.

Ví dụ nếu cần thực hiện một thao tác thay đổi trạng thái hiển thị của console thì hiển nhiên chúng ta sẽ phải nghĩ tới một procedure. Còn nếu cần một công cụ hỗ trợ thu gọn logic tính toán thì có thể chúng ta sẽ muốn sử dụng một function hơn – để đảm bảo là sẽ không có dòng code lạc nào tạo ra hiệu ứng biên side-effect không mong muốn.

procedure

Từ đầu cho tới giờ thì chúng ta đã sử dụng các procedure căn bản không có các tham số parameter. Và bây giờ chúng ta sẽ làm một ví dụ định nghĩa procedure có các tham số. Để thuận tiện thì chúng ta sẽ bổ sung thêm một procedure Increment trong package Week.

Và sau đó thử sử dụng Increment trong Main:

Ngoài yếu tố định kiểu dữ liệu của các tham số thì chúng ta có thêm các từ khóa inout đính kèm ở phía trước. Từ khóa in sẽ giúp chúng ta giới hạn chức năng của tham số Day trong code logic chi tiết của Increment, và đảm bảo sẽ không có thao tác nào tạo ra sự thay đổi trên tham số này. Trong khi đó thì từ khóa out sẽ giúp chúng ta đảm bảo rằng, procedure này sẽ bắt buộc phải tạo ra tác động thông qua tham số Increased.

Trong môi trường của C thì chúng ta có thể sử dụng từ khóa const để mô phỏng lại các tham số in, còn các tham số out thì sẽ là các con trỏ pointer.

Bây giờ giả sử dụng ta thực hiện một phép gán thay đổi giá trị của biến Day trong code ví dụ của Ada – hay day trong code ví dụ của C ở trên thì chắc chắn trình biên dịch sẽ thông báo lỗi. Tuy nhiên thì khi mô phỏng lại tham số out của Ada bằng con trỏ pointer trong C thì chúng ta không thể đảm bảo được rằng tham số đó chắc chắn phải được sử dụng. Nếu chúng ta quên không tạo ra thao tác nào tác động tới con trỏ đó thì trình biên dịch của C cũng sẽ không đưa ra nhắc nhở nào.

Bạn thấy đấy, nếu xét trên khía cạnh giao diện lập trình bậc cao thì việc đem so sánh C với Ada chắc chắn sẽ là một sự thiệt thòi cho C. Và có lẽ, khi đã biết tới Ada thì việc sử dụng C sẽ chỉ có nhiều ý nghĩa nếu như chúng ta muốn giao tiếp với các thư viện cung cấp bởi các môi trường phát triển phần mềm khác. Ví dụ đơn cử là trình điều khiển của các DBMS chắc chắn sẽ luôn có phiên bản dành cho C đầu tiên rồi sau đó mới là các phiên bản dành cho các ngôn ngữ lập trình bậc cao phổ biến. Bởi vì dù gì thì C cũng là gốc rễ của các hệ điều hành máy tính mà chúng ta đang sử dụng.

function

Bây giờ chúng ta sẽ chỉnh sửa lại Increment một chút và sử dụng định nghĩa function thay thế cho procedure.

Do các Hàm function được định nghĩa là sẽ không tạo ra thay đổi tác động tới môi trường bên ngoài nên chúng ta không cần các chỉ dẫn inout cho các tham số. Thay vào đó thì mặc định tất cả các tham số được truyền vào sẽ đều là in và không có bất kỳ thao tác nào dạng như phép gán giá trị được cho phép. Nếu muốn thực hiện các thao tác tính toán có logic phức tạp, chúng ta sẽ phải khai báo thêm các biến cục bộ để sử dụng.

(chưa đăng tải) Procedural Programming + Ada, Bài 5 – Types & Attributes

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo