banner

[Rule] Rules  [Home] Main Forum  [Portal] Portal  
[Members] Member Listing  [Statistics] Statistics  [Search] Search  [Reading Room] Reading Room 
[Register] Register  
[Login] Loginhttp  | https  ]
 
Messages posted by: NQL05  XML
Profile for NQL05 Messages posted by NQL05 [ number of posts not being displayed on this page: 0 ]
 

alice wrote:
24. Vấn đề thực thi cuối cùng mà mình đề cập là phép tính nghịch đảo theo phép nhân modulo 2↑w (modular multiplicative inverse) cần thiết để giải mã. Phép tính này có thời gian tính toán không phải là O(1) mà là O(w) ...

...

Code:
word inverse(word a)
{
word b = a;
for(int w=30; w; w--)
{
b *= b;
b *= a;
}
return b;
}


 


Có cách khác nhanh hơn chỉ mất O(log w) và cũng rất secure:

Code:
word inverse(word a)
{
word b = 2 - a; // ab == 1 mod 4
b *= 2 - a*b; // ab == 1 mod 16
b *= 2 - a*b; // ab == 1 mod 256
b *= 2 - a*b; // ab == 1 mod 65536
b *= 2 - a*b; // ab == 1 mod 4294967296
return b;
}

congai582 wrote:
Anh chị nào đã từng nghiên cứu phương pháp mã hoá khối RC6 có thể cho em hỏi vài câu được ko ạ
Em có một vài ví dụ đọc trong sách :Internet Security Cryptographic Principles, Algorithms and Protocols".
Trong phần mở rộng khoá, họ viết rằng
Example 3.13
Consider RC5-32/12/16. Since w = 32, r = 12 and b = 16, we have

u = w/8 = 32/8 = 4 bytes/word
c = [b/u] = [16/4] = 4 words
t = 2(r + 1) = 2(12 + 1) = 26 words

The plaintext and the user’s secret key are given as follows:
Plaintext = eedba521 6d8f4b15
Key = 91 5f 46 19 be 41 b2 51 63 55 a5 01 10 a9 ce 91

Key expansion

Two magic constants
P32 = 3084996963 = 0xb7e15163
Q32 = 2654435769 = 0x9e3779b9


P32 = 3084996963 và Q32 = 2654435769 thì em hiểu, nhưng em ko hiểu làm sao họ lại làm ra được dãy số 0xb7e15163, 0x9e3779b9 bằng phương pháp nào nữa.

với
Pw = Odd ((e − 2)2w)
Qw = Odd ((φ − 1)2w)
where
e = 2.71828 . . . (base of natural logarithms)
φ = (1 + √5)/2 = 1.61803 . . . (golden ratio)
Odd(x) is the odd integer nearest to x.


và phép toán quay trái <<< và phép toán XOR trong các phần mã hoá và giải mã, sẽ chuyển qua dạng nhị phân để xoay và tính toán hay sao ạ.
Rất mong nhận được sự giúp đỡ của các anh chị 


Bạn nên tìm những quyển sách căn bản về nguyên lý máy tính, vi xử lý, lập trình C,... mà đọc. Chứ quyển này bạn đọc bây giờ chỉ vô ích mà thôi.

mr_hoang09 wrote:
chào anh(chị)!
có anh chị nào có tài liệu về lập trình thời gian thực ko thì share cho em với, em cần gấp lắm.
cảm ơn nhiều! 


Bạn hãy search và download tất cả các tài liệu và công cụ của ngôn ngữ AADL (Architectural Analysis & Design Language, ngôn ngữ phân tích thiết kế kiến trúc).
RESOURCES:

https://libre2.adacore.com/ -- Môi trường phát triển tích hợp GNAT 2006. Ấn bản GPL Edition là mã nguồn mở và miễn phí, cho phép download binary và mã nguồn cho 3 loại host: x86-Windows, x86-Linux và ppc-Darwin. Mỗi loại cho phép viết phần mềm cho target cùng kiểu vi xử lý với host và cùng hệ điều hành với với host (self-hosted development) hoặc không có hệ điều hành (bare board cross-development). Ấn bản Academic (học đường) và Pro (thương mại) cho phép phát triển không hạn chế target.

http://www.cs.york.ac.uk/ftpdir/reports/YCS-2003-348.pdf Burns A. et al.: "Guide for the Use of the Ada Ravenscar Profile in High Integrity Systems." York, United Kingdom: University of York, Jan. 2003. Technical Report YCS 348. -- Giới thiệu kỹ thuật Rate Monotonic Analysis (RMA) và Response Time Analysis (RTA). Mã nguồn mẫu dùng Ravenscar, một phần hạn chế của Ada thích hợp cho lập trình hệ thống high integrity real-time embedded.

Kết phần 2

Đó là cái nhìn Ada đối với luồng và lập trình song song, một cái nhìn mà từ những ngôn ngữ thông thường có lẽ bạn không được biết. Cá nhân tác giả cho rằng Ada trong lập trình song song là một ngôn ngữ mạnh khác thường, việc sử dụng nó sẽ hạn chế được nhiều lỗi, mà đó là những lỗi cực kì khó chịu trong mảng lập trình này.

Câu chuyện về Ada và lập trình an toàn xin được chấm dứt ở đây. Tác giả hi vọng bạn đọc tìm thấy qua đó những thông tin nếu không bổ ích thì ít ra cũng là thú vị smilie.


AUTHOR: Marek Paška (17)

TRANSLATOR: N.Q.L. (18 )



__________________________
NOTES

(17) paskma @ students.zcu.cz

(18 ) Được sự chỉ bảo, chỉnh lí của các bạn Bảo Ngọc, Vân Anh, N.X. và N.Z.
Người đọc – người viết

Người đọc – người viết (readers & writers) là một vấn đề tiêu biểu của lập trình song song, thực chất như sau. Cho một đối tượng toàn cục trên đó có nhiều luồng làm việc. Có hai loại luồng. Loại thứ nhất, người đọc, là những luồng chỉ đọc đối tượng ấy. Loại thứ hai, người viết, là những luồng có thể đọc và viết vào đối tượng ấy. Khi trên đối tượng chỉ có nhiều người đọc làm việc thì không có vấn đề gì. Nhưng khi trên đối tượng có một người viết và bắt đầu viết vào nó thì có thể làm cho dữ liệu không nhất quán trong một khoảnh khắc nào đó. Nếu trong khoảnh khắc bất hạnh ấy, một người đọc tiến hành đọc dữ liệu thì hậu quả thật khôn lường. Nếu có nhiều người viết cùng viết vào đối tượng thì tình hình còn tồi tệ hơn.

Vậy chúng ta phải đảm bảo sao cho tại mỗi thời điểm, dữ liệu không cho phép nhiều người viết cùng làm việc, không cho phép người đọc và người viết cùng làm việc, chỉ cho phép nhiều người đọc, hoặc một người viết làm việc.

Trong các ngôn ngữ lập trình thông thường, việc này giải quyết được bằng một mutex và một semaphore. Còn trong Ada, giải pháp đã được thiết đặt ngay trong các kết cấu của ngôn ngữ.

Giải pháp ấy là các kiểu có bảo vệ (protected). Trong đoạn nói về phân trình và tham số, ta đã biết rằng hàm trong Ada chỉ được phép đọc mà không được phép viết vào tham số của mình, còn thủ tục thì được phép vừa đọc vừa viết. Nhất quán với nguyên tắc đó, các tác giả Ada đã xây dựng ngữ nghĩa cho các kiểu có bảo vệ. Một kiểu có bảo vệ gồm có dữ liệu và một bộ các phân trình làm việc trên dữ liệu. Trong đó, hàm chỉ được phép đọc dữ liệu, thủ tục thì còn được phép viết dữ liệu. Bộ thừa hành (run-time) đảm bảo khi đang thi hành một thủ tục của đối tượng có bảo vệ thì không thi hành bất cứ phân trình nào khác. Nói cách khác, chuỗi hành động trong thủ tục ấy tương đương với một hành động không thể chia nhỏ được (atomic). Thí dụ sau đây trình diễn một kiểu tài khoản an toàn với đa luồng.

Code:
protected type Account is -- spec
procedure Deposit (A : Money);
procedure Withdraw (A : Money);
function Balance return Money;
private
Current_Balance : Money := 0.0;
end Account;
protected body Account is
procedure Deposit (A : Money) is
begin
Current_Balance := Current_Balance + A;
end;
procedure Withdraw (A : Money) is
begin
Current_Balance := Current_Balance - A;
end;
function Balance return Money is
begin
return Current_Balance;
end;
end Account;

Khai báo được tách rời thành hai phần: phần đặc tả (spec) xác định giao diện, và phần thân (body) chứa chi tiết thực thi. Trên một biến toàn cục kiểu Account, nhiều luồng có thể đồng thời gọi hàm Balance và mọi chuyện diễn ra song song. Nhưng nếu một luồng nào đó gọi thủ tục Deposit hay Withdraw, nó phải chờ mọi luồng khác hoàn thành công việc của mình. Các luồng gọi thủ tục là người viết, còn các luồng gọi hàm là người đọc. Việc phân loại luồng như vậy là tùy theo thời điểm. Một luồng có thể lúc là người đọc, lúc khác là người viết. Thí dụ:
Code:
declare
A : Account;
-- khai báo nhiều tác vụ làm việc với A
begin
if A.Balance >= 10_000_000.0 then
A.Withdraw(100_000.0);
end if;
end;


Khi một tác vụ chỉ muốn gửi dữ liệu cho tác vụ khác mà chúng không muốn đồng bộ với nhau, dữ liệu được gửi qua một bộ đệm (buffer) là bộ nhớ kiểu FIFO. Nếu chỉ có cơ cấu điểm hẹn thì ta sẽ phải tạo ra thêm một tác vụ thứ ba nữa quản lý bộ đệm. Nhưng với cơ cấu kiểu có bảo vệ ta chỉ cần khai báo bộ đệm là một kiểu có bảo vệ và không cần đến tác vụ thứ ba nào nữa. Kiểu có bảo vệ giúp làm giảm số lượng tác vụ một cách đáng kể. Hơn nữa, hàm và thủ tục bảo vệ có thể thi hành trong khung cảnh (context) của tác vụ nào cũng được, nhờ đó giảm rất nhiều số lần chuyển cảnh (context switch). Kiểu có bảo vệ là một cơ cấu mới hết sức hiệu quả để giải quyết an toàn vấn đề liên lạc không đồng bộ và còn hàng loạt vấn đề khác của lập trình song song.
Rendezvous

Tác vụ viết ở trên có phần đặc tả rỗng, nó không cho phép các tác vụ tương tác với nhau. Phân chia chương trình thành nhiều tác vụ cũng giống như phân công nhiệm vụ trong một cơ quan – các tác vụ càng độc lập với nhau càng tốt nhưng sự tương tác giữa hai và nhiều tác vụ là một điều hiển nhiên không thể tránh. Ada giải quyết vấn đề tương tác bằng một cơ cấu thật ngoạn mục. Nó dựa trên quan niệm là hai tác vụ muốn trao đổi dữ liệu với nhau thì cả hai phải cùng đồng ý, phải làm thế nào đó thỏa thuận được với nhau – nói cách khác chúng phải đồng bộ với nhau. Theo quan niệm này thì trao đổi dữ liệu và đồng bộ hóa là không thể tách rời. Cơ cấu này trong Ada được gọi là điểm hẹn (rendezvous) (15). Để tiến tới điểm hẹn, một trong hai tác vụ (server) sẽ “bày hàng” và “chờ đợi” tác vụ kia (client) gọi tới mình. Ta sẽ sửa đổi mã nguồn trên để chúng làm một phép tính gì đó hữu ích, chẳng hạn như là sắp xếp mảng. Tác vụ server cần phải cung cấp hai dịch vụ: tiếp nhận dữ liệu và giao trả kết quả.

Code:
task type QuickSort_Task is
entry Input (A : in Table);
entry Output (A : out Table);
end QuickSort_Task;
task body QuickSort_Task is
Buffer : Table;
begin
accept Input (A : in Table) do
Buffer := A;
end Input;
QuickSort(Buffer);
accept Output(A : out Table) do
A := Buffer;
end Output;
end QuickSort_Task;


Và client (trong trường hợp này là chương trình chính) gọi server đại khái như sau.
Code:
declare
T1, T2 : QuickSort_Task;
A, B : Table;
begin
A :=...;
B :=...;
T1.Input(A);
T2.Input(B);
...
T1.Output(A);
T2.Output(B);
end;


Vậy là trong phần đặc tả tác vụ có thêm hai câu khai báo bắt đầu bằng từ khóa entry. Qua đó tác vụ cho biết khả năng tương tác với môi trường xung quanh. Tương ứng trong thân tác vụ xuất hiện hai lệnh ký hiệu bởi từ khóa accept, hai lệnh này thực hiện tương tác đó. Mỗi entry trong đặc tả tương ứng với đúng một lệnh accept trong thân (16). Trong chương trình chính ta tạo ra hai tác vụ (T1 và T2) kiểu QuickSort_Task, tiếp nhận dữ liệu từ luồng chính, thực hiện tính toán rồi giao trả kết quả.

Việc thi hành chương trình diễn ra như sau. Trước hết hệ điều hành khởi động luồng chính. Bộ run-time của Ada tạo ra hai tác vụ T1 và T2, nên khi bắt đầu vào điểm begin của chương trình chính ta có cả thảy 3 luồng. Tác vụ T1 và T2 tiến đến lệnh accept Input và dừng lại ở đó chờ, chờ đến khi có một luồng gọi tới entry Input. Song song với chúng, luồng chính chạy tới dòng T1.Input(A). Vì T1 đang chờ sẵn tại accept Input, việc nhận dữ liệu diễn ra. Sau đó T1 chạy tiếp một mạch tới accept Output, lại dừng và chờ tiếp. Tương tự như vậy T2 tiếp nhận dữ liệu B. Việc trả kết quả cũng hoàn toàn tương tự. Trong bất cứ trường hợp nào, nếu luồng chính đến điểm hẹn trước, nó sẽ dừng lại và chờ đối tác của mình để giao (nhận) dữ liệu. Tóm lại là luôn có một ai đó đến điểm hẹn trước và phải dừng lại một lúc chờ đối tác.

Trong kết cấu lệnh accept như trên, server không thể rời bỏ điểm hẹn mà phải chờ client đến bằng được mới thôi. Thời gian chờ như vậy là không giới hạn. Nhưng Ada còn có kết cấu cho phép diễn tả một "người tình" sốt ruột, chỉ chờ một khoảng thời gian nhất định, nếu đối tác không đến thì thôi. Hơn nữa kết cấu này còn cho phép người tình chờ đợi cùng một lúc tại nhiều điểm hẹn khác nhau smilie. Thí dụ sau đây trình diễn cả hai khả năng đó.

Code:
task type Teller is
entry Deposit (A : in Money);
entry Withdraw (A : in Money);
entry Balance (B : out Money);
end Teller;
task body Teller is
Current_Balance : Money;
begin
select
accept Deposit (A : in Money) do
Current_Balance := Current_Balance + A;
end Deposit;
or
accept Withdraw(A : in Money) do
Current_Balance := Current_Balance - A;
end Withdraw;
or
accept Balance(B : out Money) do
B := Current_Balance;
end Balance;
or
delay 10.0; -- chờ đợi 10 giây
end select;
end Hello_Task;


Tác vụ này mô phỏng một nhân viên quầy thu ngân, cung cấp 3 dịch vụ là gửi tiền (Deposit), rút tiền (Withdraw) và xem số dư tài khoản (Balance). Khi khởi động, tác vụ sẽ chờ đồng thời tại 3 điểm hẹn Deposit, Withdraw và Balance xem có ai gọi thì sẽ thực hiện dịch vụ tương ứng. Nếu sau 10 giây không có lời gọi, tác vụ kết thúc. Tất cả đều là nhờ vào kết cấu select. Thực hiện một cơ cấu tương tự bằng một đối tượng mức thấp như cờ hiệu (semaphore) chắc chắn là không khó, nhưng cần phải suy nghĩ nhiều hơn và mã nguồn sẽ không rõ ràng bằng. Việc này trong lập trình song song thường có thể dẫn đến những tình huống lỗi rất khó chịu, chỉ phát sinh trong những điều kiện nhất định hầu như không thể tái tạo được.





_______________________
NOTES

(15) Rendezvous: cuộc hẹn hò gặp gỡ.

(16) Tổng quát, mỗi entry có ít nhất một lệnh accept tương ứng.
Phần 2 của loạt bài tiếp cận lập trình đa luồng (multi-thread) và cái nhìn dưới con mắt Ada.


Mỗi con sông đều có nhiều luồng nước chảy

Các ứng dụng đa luồng thống trị thế giới phần mềm. Mỗi ứng dụng tương tác, bất kể trên desktop hay server đều cần hoạt động theo nhiều luồng để cho “cục cưng” người dùng có cảm giác mọi ý muốn của hắn đều được thỏa mãn ngay tức thì và máy tính chỉ hầu hạ cho riêng hắn. Qua phần này, không những bạn sẽ được biết về luồng nói chung, mà còn được biết các luồng hoạt động như thế nào trong Ada. Ada hỗ trợ lập trình đa luồng bằng những kết cấu đặc biệt của ngôn ngữ. Nói vắn tắt, một thứ gì đó khác hẳn luồng trong Linux, Win32, Java và .NET.

Một chút thuật ngữ
Thuật ngữ, như ta vẫn thường thấy, là một mớ bòng bong. Cùng một sự vật, các hệ thống *NIX đặt tên một đàng, Microsoft đặt tên một nẻo; thuật ngữ trong tài liệu người dùng khác xa với tài liệu lập trình viên; mỗi ngôn ngữ lập trình tự đặt ra các thuật ngữ của riêng mình. Rồi dịch thuật không thống nhất cũng làm cho vấn đề rối ren thêm nữa. Trong bài này, người viết sẽ dùng khái niệm quá trình (process) để chỉ đối tượng của hệ điều hành, chứa một hay nhiều luồng (thread). Tất cả các luồng của một quá trình dùng chung một không gian địa chỉ, nhưng mỗi luồng đều có ngăn xếp (stack) của riêng mình. Ngôn ngữ Ada đặt ra thuật ngữ tác vụ (task) mà về bản chất, đó chính là luồng.

Cơ hội?...
Hãy tưởng tượng bạn dùng một chương trình e-mail client. Sau khi bạn viết xong một lá thư và ấn nút Send, chương trình phải thực hiện một hành động nào đó. Nó phải kết nối với một e-mail server nào đó rồi chuyển lá thư đi. Việc này có thể kéo dài hàng chục giây. Nếu giả thử mọi hành động đều diễn ra trong một luồng thì chương trình client của bạn trong suốt khoảng thời gian đó sẽ “treo” cứng. Điều đó chẳng thể làm cho bạn vui lòng, bạn muốn viết một lá thư khác nữa kia mà. Đa luồng sẽ giải quyết tình huống này một cách đẹp mắt. Sau khi ấn nút Send chương trình client tạo ra một luồng mới có nhiệm vụ gửi thư. Việc tạo luồng diễn ra rất nhanh, trong nháy mắt bạn có thể bắt tay vào lá thư mới. Việc phát sinh một luồng mới nào đó chạy mất hàng chục giây để gửi thư, bạn chả bận tâm.

... Và thách đố?
Khi chạy một quá trình, hệ điều hành khởi động một luồng duy nhất. Tạo thêm những luồng mới là công việc của người lập trình. Thường là bằng cách gọi một hàm nào đó có trong thư viện của ngôn ngữ và liên hệ trực tiếp với một thủ tục (routine) tương ứng nào đó của hệ điều hành. Một hàm như thế thường tiếp nhận địa chỉ của một phân trình, lấy địa chỉ ấy làm điểm xuất phát cho luồng mới.

Rồi cần phải có các công cụ để điều khiển các luồng đã tạo. Các luồng cần trao đổi thông tin với nhau, ta thường phải đảm bảo rằng hai luồng không làm việc đồng thời trên một biến toàn cục, vân vân. Phục vụ cho việc đó là "kính thưa" các kiểu đối tượng nguyên sơ (primitive) như đoạn găng (critical section), khóa (lock), cờ hiệu (semaphore), mutex (13), mutant (14). Chúng ta sẽ không quan tâm tìm hiểu ý nghĩa của những từ này; điều quan trọng là để làm việc với những đối tượng này, có các thủ tục trong kernel hệ điều hành và, tương ứng với chúng, có các hàm trong các ngôn ngữ lập trình.

Nếu lập trình ở mức thấp nhất, người lập trình thường gọi thẳng các thủ tục của hệ điều hành. Thí dụ để tạo luồng mới Linux có pthread_create, Win32 có CreateThread. Lập trình như thế, mã nguồn sẽ không thể chuyển đặt (portable). Khó khăn này có thể xử lý bằng một thư viện che phủ tầng thấp nhất đó, nhưng chúng ta, lập trình viên lười biếng, muốn có nhiều tiện nghi hơn. Trong Java (và do đó cả trong C#) tình thế vẫn tương tự, thư viện chuẩn cung cấp một lớp nào đó nối thẳng vào tận ruột gan của Máy Ảo (Virtual Machine) và cho phép chạy một phương thức (method) trong một luồng mới. Java còn hỗ trợ đa luồng bằng cả những kết cấu làm đồng bộ luồng.

Đối tượng nguyên sơ, thủ tục kernel, thư viện hay kết cấu ngôn ngữ nói trên đều là những công cụ hỗ trợ mức thấp. Lập trình đa luồng với chúng là một công việc căng thẳng, đầy mệt mỏi và dễ gây sai sót, có thể ví như như lập trình bằng hợp ngữ vậy.

Quên đi tất cả, đến với Ada
Chính thế. Với Ada mọi chuyện đều khác. Luồng trong Ada được nhìn từ một tầm trừu tượng hóa cao hơn, chứa đựng một quan điểm toàn diện hơn nhiều. Luồng Ada, hay còn gọi là tác vụ, là một thực thể đặc biệt. Không những có thể khai báo một tác vụ đơn lẻ mà còn có thể khai báo kiểu tác vụ, một thứ khuôn mẫu để từ đó tạo ra nhiều tác vụ giống nhau. Ta hãy bắt đầu với một Hello World đa luồng.
Code:
with Ada.Text_IO;
use Ada.Text_IO;
procedure Hello_Example is
task type Hello_Task is -- đặc tả tác vụ
-- giao diện (rỗng)
end Hello_Task;
task body Hello_Task is -- thân tác vụ
begin
Put_Line("Hello from thread");
end Hello_Task;
H1, H2, H3 : Hello_Task; -- 3 tác vụ (3 luồng) giống nhau
-- song song với luồng chính
begin -- Tại đây, cả 4 luồng cùng khởi động
Put_Line("Hello from main thread");
end Hello_Example;


Đây là một chương trình đầy đủ, chạy được. Ở đầu ta xác lập một kiểu Hello_Task, thể hiện một luồng nào đó làm khuôn mẫu. Định nghĩa của kiểu này được chia thành hai phần rời hẳn nhau: đặc tả (specification) và thân (body). Đặc tả ở đây rất đơn giản, thực chất chỉ có mỗi cái tên. Thân cũng rất đơn giản, chỉ viết ra câu “Hello from thread” rồi kết thúc. Kế đó ta khai báo ba biến (H1, H2 và H3) của kiểu Hello_Task, cũng y hệt như mọi biến khác mà thôi. Câu khai báo này cho biết ta cần ba tác vụ kiểu Hello_Task và hệ thống sẽ khởi động chúng khi bắt đầu vào đến thân chương trình, tức là điểm begin kế đó. Khi luồng chính bắt đầu viết ra câu “Hello from main thread” thì đã có cả thảy bốn luồng cùng chạy song song. Trên màn hình sẽ hiển thị bốn dòng sau đây, theo một thứ tự ngẫu nhiên nào đó.

Code:
Hello from thread
Hello from thread
Hello from main thread
Hello from thread




______________________________
NOTES

(13) Sinh từ mutual exclusion (loại trừ lẫn nhau).

(14) Đây là đặc sản trong thực đơn tiếng lóng của Microsoft, dùng để chỉ một dị bản của mutex cho Windows NT kernel, chứ không phải là “quái nhân đột biến gene” smilie.
Kết phần 1

Tác giả hy vọng rằng đọc đến đây ai cũng cảm nhận được thế nào là kiểm tra kiểu mạnh và làm sao nó giảm nhẹ được công việc lập trình. Khi phát triển những ứng dụng thông thường, hỗ trợ từ phía ngôn ngữ như thế là phương tiện phòng bị chống “giảm thọ, hao tài”. Khi phát triển các ứng dụng liên quan đến sinh mạng con người, hỗ trợ ấy là vũ khí vô cùng thiết yếu.

Rõ ràng, những gì tác giả đã viết ra đây không phải là những phát pháo hoa mới nhất về ngôn ngữ lập trình, mà là những quan niệm cổ điển chân thiện nhất, đã được giũa mài đến mức hoàn mỹ.
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 inout. 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 (inin 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 (outin 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.
Mảng và vòng lặp

Mảng trong Ada mẫu mực lắm, chúng chả gây ngạc nhiên là bao cho người dùng Pascal và C. Lại xin trình diễn một đoạn mã làm thí dụ.
Code:
declare
type Mang_ky_tu is array (1..100) of Character;
type Ma_tran is array(1..100, 1..100) of Float;
S, S2 : Mang_ky_tu;
M, M2 : Ma_tran;
begin
S(1) := ‘A’; -- gán 1 kí tự
S2 := S; -- gán toàn bộ mảng
S(11..15) := S(1..5); -- gán một phân mảng (slice)
S(1..5) := "Hello"; -- xâu kí tự là mảng 1 chiều
M(55, 55) := 100.0; -- chỉ số 2 chiều
end;


Mảng được đánh chỉ số bằng cặp ngoặc tròn. Điều đó thoạt nhìn thì khá điên rồ nhưng dĩ nhiên có lợi ích của nó. Nhờ đó mà có thể dễ dàng đổi truy cập mảng thành lời gọi hàm (ánh xạ), thật là một cách che giấu chi tiết thực thi hoàn hảo. Trong Ada, có thể gán cả một mảng mà chả cần dùng tới vòng lặp for nào cả. Cũng đơn giản như thế, có thể làm việc với các phân mảng (slice), nghĩa là một “mảng con” bao gồm các phần tử đứng cạnh nhau trong mảng. Các xâu ký tự được định nghĩa hoàn toàn tự nhiên – nhờ mảng ký tự.

Tất nhiên, mảng cũng có những thuộc tính của nó. Thuộc tính First cho chỉ số nhỏ nhất, thuộc tính Last cho chỉ số lớn nhất. Đặc biệt hữu ích là thuộc tính Range, cho khoảng chỉ số, rất tiện dùng để lần lượt duyệt qua tất cả các phần tử của mảng. Xin trình diễn một vòng lặp for của Ada trước đã.
Code:
for i in 1..20 loop
-- hành động lặp (khi chạy sẽ được lặp 20 lần)
end loop;


Vòng lặp for là như thế đó smilie. Biến i không được khai báo ở đâu cả, kiểu của nó được tự động xác định theo khoảng đi sau từ khóa in. Vòng lặp không được giới hạn trong cặp từ khóa beginend mà được giới hạn trong cặp loopend loop, nhờ đó một mã nguồn tương đối dài sẽ dễ đọc hơn. Và cuối cùng thì ta cũng được xem màn “foreach”.
Code:
for i in S’Range loop
-- hành động trên phần tử S(i)
end loop;


Thật phong nhã và an toàn. Lập trình viên chẳng cần phải xác định xem mảng được đánh chỉ số từ 0 hay từ 1, cũng chẳng cần phải biết cận trên bằng bao nhiêu. Điều đó không có gì ấn tượng so với những chuẩn mực ngày nay. Đa số chúng ta đều dùng những lớp container tinh xảo của C# / Java / C++ STL, nơi ta không phải lo ngại về việc tràn chỉ số. Nhưng bạn phải tưởng tượng được rằng mã nguồn trên có thể biên dịch được cho một máy đích đơn chip nào đó nằm mơ cũng chả thấy Java thì bạn mới nhận ra sự phong nhã ấy giá trị biết nhường nào.

Từ những kết cấu thuần túy cho mục đích an toàn, trong ba đoạn tiếp theo đây chúng ta sẽ chuyển hướng sang các tính năng khác. Chúng không những giúp cho mã nguồn an toàn hơn và dễ đọc hơn, mà còn làm cho công việc lập trình dễ chịu hơn. Hy vọng rằng các lập trình viên chỉ quen với lối lập trình “hoang dã” có thể tìm thấy trong đó những điều lý thú.


Thuộc tính

Trước hết xin được làm rõ thuật ngữ. Trong ngữ cảnh lập trình định hướng đối tượng, khái niệm thuộc tính (attribute) được dùng theo nghĩa biến thành viên, tức là biến gắn liền với một lớp kiểu nào đó. Còn ở Ada, khái niệm này mang một ý nghĩa khác.

Các thuộc tính trong Ada là một thứ thông tin phụ trợ nào đó gắn liền với một thực thể (kiểu, biến,...). Chúng mô tả tính chất của thực thể đáng quan tâm đối với lập trình viên. Các thuộc tính Ada có lẽ gần giống nhất với thuộc tính trong C#, nhưng không tinh vi phức tạp bằng C#. Nếu nhận thức rằng Ada trên một phương diện nào đó cạnh tranh với ngôn ngữ C, các thuộc tính như vậy là quá đủ. Tốt nhất ta hãy xem một thí dụ nhỏ. Trong đoạn trước ta đã xác lập các kiểu số nguyên với những ràng buộc khác nhau.

Code:
type Integer is range -2**31 .. 2**31-1;
subtype Natural is Integer range 0 .. 2**31-1; -- số tự nhiên, kể cả số 0
subtype Positive is Integer range 1 .. 2**31-1; -- số nguyên dương


Nhưng đoạn mã này còn có một thiếu sót. Các phân kiểu Natural và Positive được chặn dưới đúng như yêu cầu, nhưng lại được chặn trên bởi những “số quái” (magic number) không diễn đạt thật rõ là chúng có cùng cận trên với Integer. Vấn đề này có thể giải quyết bằng một hằng số, nhưng dùng thuộc tính thì sẽ đẹp hơn. Các thuộc tính trong Ada được viết sau một dấu nháy đơn (apostrophe).
Code:
type Integer is range -2**31 .. 2**31-1;
subtype Natural is Integer range 0 .. Integer’Last; -- số tự nhiên, kể cả số 0
subtype Positive is Integer range 1 .. Integer’Last; -- số nguyên dương

Mỗi kiểu số nguyên đều có thuộc tính Last, giá trị của nó là số lớn nhất của kiểu. Giả sử lúc này ta muốn thay đổi khoảng giá trị của Integer, thí dụ, thành số 64 bit, thì chỉ cần thay đổi ở một nơi trong chương trình - ở nơi định nghĩa kiểu. Thuộc tính cho biết giá trị nhỏ nhất của kiểu gọi là thuộc tính First.

Xin nêu thêm vài thí dụ đáng chú ý nữa. Như đã nói trong một đoạn trước, Ada cho phép xác lập các kiểu số nguyên theo cách lập trình viên ấn định khoảng giá trị, còn bộ biên dịch tự chọn cách biểu diễn “vật lý” thích hợp. Muốn biết nó đã chọn như thế nào, ta dùng thuộc tính. Hãy xem câu khai báo xác lập một kiểu với khoảng giá trị kỳ quái sau đây.
Code:
type BlackHat is range 200 .. 1_000;

Thuộc tính Size cho biết số bit cần thiết để biểu diễn mọi giá trị của kiểu. Biểu thức BlackHat’Size có giá trị là 10 vì 1024 = 2**10. Do phần lớn máy tính đều không có phép tính số học 10 bit, bộ biên dịch phải biểu diễn mỗi kiểu dựa trên một kiểu nào đó tự nhiên (native), sẵn có trên loại máy tính của ta, gọi là kiểu cơ sở (base). Kiểu cơ sở không có tên mà chỉ được mệnh danh bằng thuộc tính Base. Biểu thức BlackHat’Base’Size có giá trị là 16, vậy BlackHat được chứa trong một thanh ghi số nguyên 16 bit.

Ngôn ngữ định nghĩa hàng loạt thuộc tính. Chúng có mặt không những ở các kiểu số nguyên, mà còn ở liệt kê (enumeration), số thực dấu chấm động và tĩnh, mảng (array), thẻ (record), tác vụ (task) và vài thứ khác liên quan.
Toán tử nhiệm mầu

Hãy tưởng tượng ta làm một ứng dụng (như là phần mềm cho vệ tinh viễn thám chẳng hạn smilie ) tính diện tích các vùng lãnh thổ nào đó. Ta muốn thiết lập ràng buộc theo tinh thần các định luật vật lý.
Code:
type Metres is new Float;
type Metres_vuong is new Float;

Nếu lúc này ta muốn tính một diện tích thì ta buộc phải đổi kiểu tường minh; lối viết này có đôi phần khiếm nhã.
Code:
declare
chieu_dai : Metres := 10.0;
chieu_rong : Metres := 15.0;
dien_tich : Metres_vuong;
begin
dien_tich := Metres_vuong (chieu_dai * chieu_rong); -- buộc phải đổi kiểu
end;

Rõ ràng ta biết rằng khi nhân mét với mét ta sẽ thu được mét vuông. Chẳng lẽ không có cách nào khéo léo đưa quy tắc đó vào ngôn ngữ? Có đấy! Ta giải quyết chuyện này bằng cách quá tải (overload) toán tử nhân cho kiểu Metres, sao cho kết quả là Metres_vuông:
Code:
function "*" (Left, Right : Metres) return Metres_vuong is
begin
return Metres_vuong(Float(Left)*Float(Right)); -- Float để tránh đệ quy
end;
Trong đoạn mã trên trước khi nhân ta đổi kiểu sang Float để tránh đệ quy. Khi bộ biên dịch dùng phép nhân Float, mọi chuyện đều ổn cả.

Giờ đây, không những khi tính diện tích ta không cần đổi kiểu nữa, mà thậm chí rất đúng ý ta, bộ biên dịch còn phát hiện được lỗi ở đoạn mã sai thứ nguyên vật lý sau đây.
Code:
declare
chieu_dai : Metres := 10.0;
chieu_rong : Metres := 15.0;
dien_tich : Metres;
begin
dien_tich := chieu_dai * chieu_rong; -- lỗi ngữ nghĩa tĩnh
end;


Để có những kết cấu kiểm tra kiểu mạnh đó, cái giá phải trả là bao nhiêu?

Nhiều người hẳn sẽ phản đối rằng với các kết cấu đó, mã đích phải rất dài và rất chậm. Họ đã nghĩ sai. Hàng loạt phép kiểm tra được thực hiện khi biên dịch mã nguồn. Phép kiểm tra khi chạy (run-time) chỉ có mặt ở những nơi nào cần thiết. Dễ thấy rằng nếu gán một biến số tự nhiên vào một biến số nguyên thì chẳng cần phải kiểm tra gì bởi vì các số tự nhiên là tập con của các số nguyên. Hơn nữa, mã nguồn càng chứa nhiều ràng buộc thì bộ biên dịch càng có nhiều thông tin để thi triển các chiêu thức tối ưu hóa mức cao. Bộ biên dịch GNAT được xây dựng trên back-end GCC, cho nên nó cũng biết hết cả các chiêu thức tối ưu hóa mức thấp. Về tốc độ chạy, các chương trình Ada hoàn toàn có thể sánh vai được với các chương trình C/C++. Các bộ xử lý ngày nay đều có mã lệnh (instruction) đặc biệt để kiểm tra ràng buộc khoảng số nguyên, nhờ vậy phí tổn cho việc đó trong những ứng dụng đời thực chỉ đáng vài phần trăm mà thôi. Nói cho cùng, ngày nay các phép tính số học thực hiện rất nhanh cả trong ngôn ngữ Java và C# cơ mà.
Ràng buộc

Ràng buộc (constraint) khoảng giá trị cho các kiểu số nguyên rất có ích cho việc loại trừ sai sót trong chương trình. Thí dụ, nếu biết trước rằng trong chương trình không bao giờ có nhiều hơn 50 bác sĩ và 1000 giường bệnh và nếu muốn loại trừ số lượng âm, ta làm như sau.
Code:
type so_bac_si is new Integer range 0..50;
type so_giuong_benh is new Integer range 0..1000;

Nhờ đó bộ biên dịch sẽ phát hiện lỗi trong câu lệnh sau đây.
Code:
n_giuong_benh := 1001;

Tất nhiên bộ biên dịch có khả năng phân tích tuyệt vời nhất vẫn không phải là đấng toàn năng để phát hiện ra mọi lỗi ngay từ khi biên dịch. Nhưng trường hợp không thể phát hiện lỗi nó sẽ sinh mã đích kiểm tra. Khi thi hành chương trình, trong phép đổi kiểu, giá trị biến được kiểm tra và nếu giá trị phá ràng buộc thì chương trình sẽ gây ra ngoại lệ (Constraint_Error). Thí dụ, đoạn mã sau đây chỉ thi hành được khi n_giuong_benh không vượt quá 50.
Code:
n_bac_si := So_bac_si(n_giuong_benh); -- có thể gây ngoại lệ Constraint_Error

Thí dụ với số bác sĩ và số giường bệnh rất rõ ràng nhưng hơi thiếu thực tế. Vì vậy xin nêu thêm một thí dụ nữa trình diễn các kiểu số nguyên xác lập sẵn theo chuẩn Ada. Ngoài kiểu ra, ta sẽ đưa vào thí dụ một tiết mục đặc biệt của Ada: phân kiểu (subtype).
Code:
type Integer is range -2**31 .. 2**31-1;
subtype Natural is Integer range 0 .. 2**31-1; -- so tự nhiên, bao gồm cả số 0.
subtype Positive is Integer range 1 .. 2**31-1; -- số dương


Các phân kiểu sẽ có ích ở nơi ta cần ràng buộc một khoảng giá trị thuộc một kiểu nào đó, nhưng lại không muốn đặt ra kiểu mới. Mọi biến thuộc một kiểu nào đó, dẫu có phân kiểu khác nhau vẫn có thể gán trị cho nhau mà không cần phải đổi kiểu tường minh. Mã đích đương nhiên vẫn thường xuyên kiểm tra giá trị và gây ngoại lệ cho trường hợp một giá trị nào đó định phá ràng buộc.

Nếu giờ ta cần một danh sách liên kết (linked list) chẳng hạn thì ta không bao giờ khai báo biến n_elements (số phần tử) là Integer, mà nên dùng Natural. Nhờ đó ta đưa được thêm thông tin vào mã nguồn và giảm được khả năng lầm lỗi. Sẽ không bao giờ số phần tử giảm xuống dưới 0. Sẽ không bao giờ việc nhặt phần tử khỏi danh sách rỗng có thể thi hành. Đây là một thí dụ lấy từ thực tế, khi bộ biên dịch đã báo một lỗi mà ở ngôn ngữ C mọi chuyện có thể kết thúc bằng một Access Violation hay Segmentation Fault thật hoạt kê. Mà chuyện đó cũng có thể chẳng hoạt kê chút nào khi thay vì có thể đi ngủ lúc 11 giờ đêm ta phải thức đến 3 giờ sáng để tìm rối và gỡ rối smilie.

Xin nói thêm, Ada cho phép xác lập ràng buộc khoảng trên cả các kiểu số dấu chấm động (floating-point), số dấu chấm tĩnh (fixed-point) và số dấu chấm tĩnh thập phân (decimal). Thí dụ:
Code:
-- Số dấu chấm động:
type Real is digits 10; -- không ràng buộc
type He_so is digits 8 range -1.0 .. 1.0; -- có ràng buộc
type Khoi_luong is digits 7 range 0.0 .. 1.0E35;
type Goc is new Real range 0.0 .. 2.0 * pi;
subtype Xac_suat is Real range 0.0 .. 1.0; -- có ràng buộc
-- Số dấu chấm tĩnh:
type Volt is delta 0.125 range 0.0 .. 255.0; -- có ràng buộc
type Fraction is delta System.Fine_Delta range -1.0 .. 1.0;
-- Fraction’First = 0.0
-- Fraction’Last = 1.0 – System.Fine_Delta
-- Số dấu chấm tĩnh thập phân:
type Money is delta 0.01 digits 18; -- có ràng buộc
-- Money’First = -10.0**16 + 0.01
-- Money’Last = 10.0**16 – 0.01
subtype Salary is Money digits 10; -- có ràng buộc
-- Salary’First = -10.0**8 + 0.01
-- Salary’Last = 10.0**8 – 0.01

Kiểm tra kiểu mạnh

Có nhiều người lầm tưởng rằng ngôn ngữ C có kiểm tra kiểu mạnh. So với những ngôn ngữ script thì quả thật C có kiểm tra kiểu ở một chừng mực nào đó, nhưng các phép đổi kiểu tự động đã giáng cho an toàn kiểu một đòn thật là trí mạng. Kiểm tra kiểu trong Java hay là ngay cả Pascal cũng chỉ là những giải pháp nửa vời.

Chính hệ thống kiểm tra kiểu mạnh với việc cấm đổi kiểu tự động là con đường dẫn đến phát triển nhanh và rẻ những ứng dụng an toàn và tin cậy. Hệ thống kiểu của Ada dựa trên nguyên tắc là các biến khác kiểu nhau không thể chuyển đổi qua lại cho nhau một cách mặc nhiên, mà chỉ có thể chuyển đổi một cách tường minh.

Thí dụ, có thể xác lập một kiểu số nguyên 32 bit mẫu mực bằng câu khai báo đại loại như sau:
Code:
type Integer is range -2**31 .. 2**31-1;


Thật quá bình dị phải không? Bộ biên dịch sẽ chọn cho khoảng đã cho một biểu diễn thích hợp nhất và sẽ kiểm tra tràn số. Mọi chuyện sẽ trở nên thú vị hơn khi ta bắt đầu xác lập kiểu dữ liệu cho một mục đích cụ thể nào đó. Thí dụ:
Code:
type so_bac_si is new Integer;
type so_giuong_benh is new Integer;


Đây là khai báo tương tự như typedef của C. Nhưng dĩ nhiên hai câu khai báo này không đặt ra tên mới cho một kiểu đã có sẵn mà xác lập ra hai kiểu mới dẫn xuất từ Integer, kế thừa các tính chất và phép toán của Integer. Các biến của kiểu so_bac_si và so_giuong_benh không tương thích với nhau. Thí dụ, do không thể gán số_bác_sĩ cho số_giường_bệnh mặc dù hai kiểu ấy được biểu diễn vật lý giống hệt như nhau, mã nguồn sau đây không thể biên dịch được.
Code:
declare
n_bac_si : so_bac_si := 10;
n_giuong_benh : so_giuong_benh;
begin
n_giuong_benh := n_bac_si; -- lỗi ngữ nghĩa tĩnh
end;


Như vậy ngôn ngữ đảm bảo rằng trong chương trình không lẫn lộn được số bác sĩ với số giường bệnh; nhưng nếu điều đó là thật sự cần thiết thì có thể viết một cách tường minh. Thí dụ, câu lệnh sau đây không thể là một sai sót vô tình của lập trình viên mà chỉ có thể là chủ ý rõ ràng, hoặc là sự phá hoại rành rành:
Code:
n_giuong_benh := so_giuong_benh(n_bac_si); -- đổi kiểu tường minh
Lập trình an toàn với Ada


Ada là ai?

Ada là ngôn ngữ lập trình xuất xứ từ Bộ quốc phòng Mỹ vào khoảng nửa đầu thập niên 80 của thế kỷ 20. Ngôn ngữ này được đặt tên theo Ada Augusta nữ bá tước xứ Lovelace (1815 – 1852), nhà toán học với ý tưởng tiên phong coi phần cứng và phần mềm là hai mặt khác nhau đã đi vào lịch sử như lập trình viên đầu tiên và hacker đầu tiên của loài người (1).

Nhu cầu của Lầu Năm Góc lúc đó là một ngôn ngữ lập trình duy nhất thay thế cho khoảng 500 ngôn ngữ dùng cho các hệ thống nhúng (embedded). Đặc trưng của các hệ thống như thế là phần cứng đa dạng, phần cứng liền với phần mềm, phần mềm hệ thống liền với phần mềm ứng dụng, thời gian sử dụng lâu dài, tính chất thời gian thực (real-time) và độ bền cao (high-integrity) thể hiện rõ, nhiều hệ thống nhúng là hệ phân tán (distributed).

Ngôn ngữ lập trình đáp ứng những yêu cầu đó phải chặt chẽ, nhỏ gọn nhưng có sức biểu diễn lớn, viết mã nguồn dễ đọc, sinh mã đích hiệu quả.

Vì thế Ada xuất phát từ Pascal, nhưng kiểm tra kiểu mạnh hơn. Mở rộng kiểu (type extension), kế thừa (inheritance) và đa kế thừa giao diện (multiple interface inheritance) hỗ trợ lập trình định hướng đối tượng. Ngoài các kết cấu điều khiển thông thường như rẽ nhánh, lặp, xử lý ngoại lệ (exception), và kết cấu đơn vị thông thường như thủ tục (procedure), hàm (function), Ada còn có kết cấu gói (package)(2) hỗ trợ lập trình modular, kết cấu mẫu (generic)(3) hỗ trợ lập trình mẫu, kết cấu tác vụ (task)(4) và kiểu có bảo vệ (protected type)(5) hỗ trợ lập trình song song và tương tranh.

Ngoài các ứng dụng truyền thống trong vũ khí, khí tài và các hệ thống liên lạc, tham mưu, chỉ huy, tác chiến, sau hơn 2 thập kỷ ngày nay Ada còn được dùng trong các ứng dụng của ngành thám hiểm không gian, hàng không, giao thông sắt & bộ, năng lượng hạt nhân, viễn thông, và tài chính – ngân hàng. Xem (6).

Trên thế giới, tỉ lệ lập trình viên sử dụng ngôn ngữ Ada chiếm khoảng 5%. Xem (7).

Ada có bộ biên dịch miễn phí GNAT và môi trường phát triển miễn phí GPS, sinh mã đích cho rất nhiều platform khác nhau.

Loạt bài này hoàn toàn không có tham vọng dạy cho ai đó lập trình bằng ngôn ngữ Ada. Một bài tổng quan về Ada cũng dài hơn 100 trang (8 ). Ngôn ngữ này nếu muốn học đầy đủ cần phải đọc vài quyển sách dày (9, 10). Dẫu vậy, để cho mọi người đều thấy Ada không phải là một ngôn ngữ gì quái dị mà ngược lại, hết sức bình dị, xin trình diễn một màn Hello World nho nhỏ.

Code:
with Ada.Text_IO;
use Ada.Text_IO;
procedure Hello_Main is
begin
Put_Line("Hello world!");
end;




______________________________
NOTES
(1) Ada Augusta Lovelace đã giúp Charles Babbage làm dự án xây dựng Analytical Engine, chiếc máy tính lập trình được đầu tiên. Xem http://www.fourmilab.to/babbage/contents.html.

(2) Tương tự module trong Modula hoặc unit trong Borland Pascal.

(3) Tương tự generic trong C# hoặc template trong C++.

(4) Tương ứng với quá trình (process) hay luồng (thread), khái niệm thường gặp khi nói về các hệ điều hành.

(5) Xuất phát từ monitor (Modula), nhưng kết cấu này đã được phát triển thêm cú pháp và ngữ nghĩa phản ánh những kết quả nghiên cứu mới về lập trình song song.

(6) Feldman M. B.: Who's Using Ada?
Real-World Projects Powered by the Ada Programming Language. 2002. http://www.seas.gwu.edu/~mfeldman/ada-project-summary.html.

(7) Smith J.: What About Ada? The State of the Technology in 2003. Technical Note. Sofware Engineering Institute of Carnegie Mellon University 2003.

(8 ) Feldman M. B.: Ada 95 in context. George Washington University 1999. Adapted and updated from Chapter 10, Handbook of Programming Languages, vol. 1, Object-Oriented Languages. Macmillan 1998.

(9) Barnes J. G. P.: Programming in Ada 95, 2nd ed. Addison-Wesley. 1998.

(10) Burns A., Weillings A.: Concurency in Ada, 2nd ed. Cambridge University Press. 1998.
 

Powered by JForum - Extended by HVAOnline
 hvaonline.net  |  hvaforum.net  |  hvazone.net  |  hvanews.net  |  vnhacker.org
1999 - 2013 © v2012|0504|218|