Tuy nhiên, bằng sự đặc tả các thuộc tính thuộc về toán học và logic của một kiểu dữ liệu hoặc cấu trúc, loại dữ liệu trừu tượng này là một nguyên tắc chỉ đạo hữu dụng cho những người cài
Trang 1TRƯỜNG CAO ĐẲNG NGHỀ ĐẠI HỌC BÁCH KHOA
Báo cáo
Nhóm 2: Dịch nội dung tài liệu
Data Structures Using C And C++
Page 13 – Page 27.
Môn: Anh Văn Chuyên Ngành
Sinh viên thực hiện: Hà Tiến Dũng
Nguyễn
Duy Bình
Đỗ Trung Đông Nguyễn Trường Giang
Đinh Sơn Hải
Lớp: QTM1-K4
Hà nội, 2013
Trang 2ADT (Những kiểu dữ liệu trừu tượng)
Một công cụ hữu dụng dành cho việc mô tả thuộc tính logic của một kiểu dữ liệu chính là kiểu dữ liệu trừu tượng, hay là ADT.Về cơ bản, một kiểu dữ liệu trừu tượng là một tập hợp các giá trị và các phép toán trên những giá trị đó Chúng tạo thành một cấu trúc toán học mà có thể được thực hiện trên một phần cứng hoặc phần mềm cấu trúc dữ liệu nào đó Thuật ngữ “Dữ liệu trừu tượng” liên kết với khái niệm toán học cơ sở để định nghĩa một loại dữ liệu
Khi định nghĩa một kiểu dữ liệu trừu tượng như là một khái niệm thuộc về toán học, chúng ta quan tâm đến vấn đề giải thuật, chứ không cần tới tính hiệu quả của không gian hay thời gian Trên thực tế, định nghĩa của một loại dữ liệu trừu tượng thì chẳng hề được đề cập tới việc thực thi những tiểu tiết chút nào Nó thậm chí còn chẳng thể thực hiện một loại dữ liệu trừu tượng đặc thù trên một phần đặc thù của phần cứng hay sử dụng một hệ thống phần mềm đặc thù Chẳng hạn, chúng ta vừa nhận ra là loại dữ liệu trừu tượng số nguyên không thể sử dụng được mọi lúc Tuy nhiên, bằng sự đặc tả các thuộc tính thuộc về toán học và logic của một kiểu
dữ liệu hoặc cấu trúc, loại dữ liệu trừu tượng này là một nguyên tắc chỉ đạo hữu dụng cho những người cài đặt và một công cụ có ích cho những lập trình viên người ước mong để sử dụng loại dữ liệu một cách đúng đắn
Có một số các phương pháp để miêu tả một loại dữ liệu trừu tượng Phương pháp
mà chúng ta sử dụng là bán chính thức và vay mượn khá nhiều từ ký hiệu của Lập trình C nhưng mở rộng ký hiệu đó ở nơi cần thiết Để minh họa khái niệm của một loại dữ liệu trừu tượng và phương pháp miêu tả của chúng ta, xem xét loại dữ liệu trừu tượng hữu tỷ, cái mà tương ứng với khái niệm toán học của một số hữu tỷ Một số hữu tỷ là một số mà có thể được biểu diễn như là thương của hai số nguyên Các phép toán thực hiện trên các số hữu tỷ mà ta định nghĩa là sự sáng tạo của một số hữu tỷ từ hai số nguyên, cộng thêm vào, nhân lên, và kiểm tra sự bằng nhau Sau đây là một phần mở đầu sự đặc tả của loại dữ liệu trừu tượng này:
Trang 3/*value definition*/
abstract typedef <integer, interger> RATIONAL;
condition RATIONAL[1] != 0;
/*operator defintion*/
abstract RATIONAL makerational(a,b)
int a,b;
precondtion b !=0;
postcondition makerational[0] == a;
makerational[1] == b;
abstract RATIONAL add(a,b) /* written a + b */
RATIONAL a,b;
postcondition add[1] == a[1] * b[1];
add[0] == a[0] * b[1] + b[0] * a[1];
abstract RATIONAL mult(a,b) /* written a * b*/
RATIONAL a,b;
postcondtion mult[0] == a[0] * b[0];
mult[1] == a[1] * b[1];
abstract equal(a,b) /*written a == b */
RATIONAL a,b;
postcondition equal == (a[0]*b[1] == b[0]*a[1]);
Một loại dữ liệu trừu tượng gồm có hai phần:
1 Một giá trị định nghĩa
2 Một toán tử định nghĩa
Giá trị định nghĩa xác định một tập các giá trị cho loại dữ liệu trừu tượng và bao gồm hai phần: Một mệnh đề định nghĩa và một mệnh đề điều kiện Chẳng hạn, giá
Trang 4trị định nghĩa cho một loại dữ liệu trừu tượng hữu tỷ cho biết rằng một giá trị hữu
tỷ gồm hai số nguyên, số thứ hai trong chúng không được bằng 0 Dĩ nhiên, hai số nguyên tố đó tạo thành một số hữu tỷ là tử số và mẫu số Chúng ta dùng ký hiệu bảng (trong ngoặc vuông) để chỉ ra các phần của một kiểu trừu tượng
Các từ khóa abstract typedef giới thiệu một giá trị định nghĩa, và từ khóa
condition được dùng để định rõ bất cứ điều kiện nào trên kiểu vừa mới định
nghĩa Trong định nghĩa này, điều kiện chỉ rõ rằng thương số không được bằng 0 Mệnh đề định nghĩa được yêu cầu bắt buộc, nhưng mệnh đề điều kiện có thể không cần thiết cho từng loại dữ liệu trừu tượng
Giá trị định nghĩa sau đây ngay lập tức đem tới toán tử định nghĩa Mỗi toán tử được định nghĩa như một hàm trừu tượng với 3 phần: Phần đầu, phần tùy ý các điều kiện trước, và phần các điều kiện sau Ví dụ, toán tử định nghĩa của loại dữ liệu trừu tượng hữu tỷ, kể cả sự tạo lập các phép toán
( makerational), phép cộng (add) và phép nhân (mult), cũng như việc kiểm tra
sự bằng nhau Nào ta cùng xem xét sự đặc tả cho phép tính nhân trước tiên, bởi vì
nó là phép tính đơn giản nhất Nó chứa đựng phần đầu và các điều kiện sau, nhưng không có các tiền điều kiện:
abstract RATIONAL mult(a,b) /*written a*b */
RATIONAL a,b;
postcondition mult[0] == a[0] * b[0];
mult[1] == a[1] * b[1];
Phần đầu của định nghĩa này là hai dòng đầu tiên, chúng thì chỉ như phần đầu một
hàm của C Từ khóa “abstract” chỉ ra rằng nó không phải một hàm C mà là một
toán tử định nghĩa của loại dữ liệu trừu tượng Chú thích bắt đầu với từ khóa mới
“written” cho biết một cách khác để viết hàm.
Điều kiện sau chỉ rõ việc tính toán như thế nào Trong một điều kiện sau, cái tên
của hàm ( trong trường hợp này, mult) được dùng để biểu thị cái kết quả của phép tính Do vậy, mult[0] đại diện cho phần tử số của kết quả, mult[1] phần mẫu số
của kết quả Đó là, nó định rõ những điều kiện nào trở thành đúng sau khi phép
Trang 5toán được thực hiện Trong ví dụ này, phần điều kiện sau chỉ rõ rằng phần tử số của kết quả của một phép nhân hữu tỷ bằng một số nguyên là kết quả của phần tử thức của hai số đầu vào, và là phần mẫu thức bằng số nguyên là kết quả của hai mẫu thức
Sự đặc tả của phép cộng rất giản đơn như sau:
a0 + b0 = a0 * b1 + a1 * b0 a1 b1 a1 * b1
Phép toán sáng tạo xây dựng một số hữu tỷ từ hai số nguyên và chứa đựng ví dụ đầu tiên của một phần tiền điều kiện Nói chung, những điều kiện trước đặc tả bất
cứ hạn chế nào mà phải được thỏa mãn trước khi phép tính có thể được thực hiện
Trong ví dụ này, điều kiện trước nói rõ rằng makerational chỉ có thể được thực
hiện khi tham số thứ hai của nó khác 0
Đặc tả phép tính bằng là có ý nghĩa hơn và phức tạp hơn về mặt khái niệm Nói một cách tổng quát, bất cứ hai giá trị nào trong một loại dữ liệu trừu tượng là
“bằng nhau” khi và chỉ khi những giá trị của các thành phần của chúng là bằng
nhau Thực vậy, nó cũng thường được cho là một phép tính bằng (và một phép tính không bằng) phép toán tồn tại và được định nghĩa theo cách đó, cho nên toán
tử định nghĩa bằng nhau rõ ràng là không bắt buộc Phép toán đặt ra ( cài đặt giá trị của mục tiêu trước tới một giá trị mục tiêu khác) là một ví dụ khác của một phép tính mà thường được cho là một loại dữ liệu trừu tượng và không được đặc tả một cách chi tiết
Tuy nhiên, với một vài loại dữ liệu, hai giá trị với những thành phần không bằng nhau có thể coi như bằng nhau Thật vậy, như là trường hợp với các số hữu tỷ; ví
dụ, các số hữu tỷ 1/2, 2/4, 3/6, và 18/36 tất cả đều bằng nhau dù các thành phần của chúng không bằng nhau Hai số hữu tỷ được xem như là bằng nhau nếu các thành phần của chúng là bằng nhau khi các số được giảm tới các số hạng bé nhất ( đó là, tử số và mẫu số của chúng cả hai đều được chia bới số chia chung lớn nhất) Một cách kiểm tra sự bằng hữu tỷ là giảm hai số tới các số hạng bé nhất và sau khi kiểm tra sự bằng nhau của các tử số và mẫu số Một cách kiểm tra khác là kiểm tra sự bằng nhau hữu tỷ nếu các kết quả chéo nhau (đó là, tử số một lần, mẫu
Trang 6số một lần khác) là bằng nhau Đây là phương pháp mà ta sử dụng để đặc tả sự trừu tượng của phép toán bằng
Sự đặc tả trừu tượng làm rõ vai trò của một loại dữ liệu trừu tượng như là định nghĩa hoàn toàn hợp lý của một loại dữ liệu mới Như là bộ sưu tập hai số nguyên, hai cặp được sắp xếp không bằng nhau nếu các thành phần của chúng là không bằng nhau; song là các số hữu tỷ, chúng có thể bằng nhau Không chắc rằng cứ thực hiện tính toán các số hữu tỷ là sẽ tiến hành kiểm tra sự bằng nhau, bởi thật sự tạo thành các tích trực tiếp; chúng có thể quá lớn để đại diện như các số nguyên bằng máy Rất có thể, một sự thi hành đầy đủ sẽ trước tiên giảm các dữ liệu nhập tới số hạng bé nhất và sau đó kiểm tra sự bằng nhau của thành phần.Thực vậy, một
sự tiến hành có lý sẽ khăng khăng đòi makerational, add, và mult chỉ tạo ra các
số hữu tỷ ở các số hạng bé nhất Tuy nhiên, các định nghĩa thuộc về toán học như
là các sự đặc tả của loại dữ liệu trừu tượng không cần nói đến các việc thi hành chi tiết
Thực vậy, phép thể hiện hai số hữu tỷ đó các thể được bằng nhau ngay cả khi chúng là các thành phần không bằng nhau buộc ta viết lại các điều kiện sau cho
makerational, add, và mult Đó là, nếu
m0 = = a0 * b0 m1 a1 b1
Nó không cần thiết là m0 = a0 * b0 và m1 = a1 * b1, chỉ cần là m0 * a1 * b1 =
m1 * a0 * b0 Một sự đặc tả loại dữ liệu trừu tượng chính xác hơn cho
RATIONAL ở dưới đây:
/* value definition*/
abstract typedef<<int, int> RATIONAL
condition RATIONAL[1] ! =0;
/*operator definition*/
abstract equal(a,b) /*written a == b*/
RATIONAL a,b;
postcondition equal == (a[0]*b[1] == b[0]*a[1];
Trang 7abstract RATIONAL makerational(a,b) /*written [a,b]*/
int a,b;
precondition b != 0;
postcondition makerational[0]*b == a*makerational[1]
abstract RATIONAL add(a,b) /*written a + b */
RATIONAL a,b;
postcondition add == [a[0] * b[1] + b[0] * a[1], a[1]*b[1]]
abstract RATIONAL mult(a,b) /*written a * b */
RATIONAL a,b;
postcondition mult == [a[0] * b[0], a[1] * b[1] ]
Tại đây, toán tử bằng nhau được định nghĩa trước tiên, và toán tử = = được mở
rộng sự bằng nhau hữu tỷ dùng mệnh đề written Toán tử đó được dùng sau đó để
đặc tả các phép toán hữu tỷ theo sau ( add và mult)
Kết quả của phép tính “makerational” trên các số nguyên a và b tạo ra một số
hữu tỷ mà bằng a/b, nhưng định nghĩa không đặc tả các giá trị thật sự của việc tính
toán tử thức và mẫu thức Đặc tả cho “makerational” cũng giới thiệu ký hiệu
[a,b] cho dạng hữu tỷ từ các số nguyên a và b, và sau đó ký hiệu này được dùng
trong định nghĩa add và mult.
Các định nghĩa của “add” và “mult” đặc tả rằng kết quả của chúng như kết quả
không bị nhỏ lại của phép toán tương ứng, mà các thành phần riêng lẻ không nhất
thiết phải bằng nhau
Thông báo, lần nữa, trong định nghĩa các toán tử này chúng ta không phải đang đặc tả chúng được tính toán thế nào, chỉ phải chỉ rõ được kết quả của chúng là gì Chúng được tính toán thế nào là được quyết định bởi sự thực thi của chúng, không phải bởi sự đặc tả của chúng
Trang 8Các dãy như Những giá trị định nghĩa
Trên đà phát triển các sự đặc tả cho các loại dữ liệu đa dạng, ta thường sử dụng ký hiệu tập hợp lý thuyết để đặc tả các giá trị của một loai dữ liệu trừu tượng Đặc biệt, nó giúp ích để sử dụng ký hiệu của các dãy toán học mà chúng ta giới thiệu Một dãy đơn giản là tập hợp các phần tử có thứ tự Đôi khi một dãy S được viết như sự liệt kê các phần tử của nó, như là
S = < s0, s1, , s(n-1) >
Nếu S chứa đựng n phần tử, S được nhắc đến là độ dài của n Chúng ta giả sử sự tồn tại của một hàm có chiều dài len như là len(S) chính là chiều dài của dãy S Chúng ta cũng giả sử rằng các hàm first(S), hàm mà trả về giá trị phần tử đầu tiên của S (S0 trong những điều đã đề cập đến ở ví dụ), và last(S), hàm mà trả về giá trị phần tử cuối cùng của S(Sn-1 những điều đã đề cập đến ở ví dụ).Có một dãy đặc biệt với với chiều dài 0, gọi là nilseq, dãy mà không chứa phần tử nào,
first(nilseq) và last(nilseq) đều không được định nghĩa.
Chúng ta ước muốn định nghĩa một loại dữ liệu trừu tượng stp1 loại mà những giá
trị của nó là các phần tử của các dãy Nếu các dãy có thể được tùy ý về chiều dài
và gồm có tất cả các phần tử của chúng là cùng một loại, tp, sau đó thì stp1 có thể
được định nghĩa bởi
abstract typedef <<tp>> stp1;
Ngoài ra, chúng ta cũng muốn định nghĩa một loại dữ liệu trừu tượng stp2 loại mà
những giá trị của nó là các dãy có chiều dài cố định mà các phần tử của dãy là của các loại đặc trưng Trong trường hợp này, ta sẽ đặc tả định nghĩa:
abstract typedef <tp0, tp1, tp2, … tpn> stp2;
Dĩ nhiên, chúng ta có thể muốn định rõ một dãy với chiều dài cố định mà tất cả các phần tử của nó đều là của cùng một loại Chúng ta sau đó có thể viết
abstract typedef <<tp, n>> stp3;
Trong trường hợp stp3 này đại diện cho một dãy với chiều dài n, tất cả các phần tử của nó đều là loại tp.
Chẳng hạn, bằng việc sử dụng các ký hiệu đã đề cập đến chúng ta có thể định nghĩa các loại sau đây:
Trang 9abstract typedef << int>> intseq;
/* sequence of integers of */
abstract typedef <integer, char, float> seq3;
/* squence of length 3 */
/* consisting of an integer, */
/* a character and a */
/* floating-point number */
abstract typedef <<int, 10>> intseq;
/* sequence of 10 integers */
abstract typedef <<,2>> pair;
/* arbitrary sequence of */
Hai dãy là bằng nhau nếu từng phần tử đầu tiên bằng tương ứng với phần tử thứ
hai Một dãy con là một phần chia liên tiếp của một dãy Nếu S là một dãy, hàm
sub(S,i,j) nghĩa là dãy con của S bắt đầu tại vị trí i trong S và gồm có j các phần tử
liên tiếp Do vậy, nếu T bằng sub(S,i,k), và T là dãy <t0, t1, , t(k-1)>, t0 =
s(i+1), ., t(k-1) = s(i+k-1) Nếu i không nằm ở giữa 0 và len(S) – k, sau sub(S,i,k), được định nghĩa như là nilseq
Sự nối kết của hai dãy, được viết là S+T, là dãy gồm có tất cả các phần tử của S
theo sau tất cả các phần tử của T Đôi khi ao ước để đặc tả sự đưa vào một phần tử
ngay giữa của một dãy, place(S,i,x) được định nghĩa như dãy S với phần tử x ngay
lập tức được chèn vào vị trí tiếp sau i (hoặc vào trong phần tử đầu tiên của dãy nếu
i là -1) Tất cả các phần tử của dãy con vị trí của chúng chuyển đi Đó là, place(S,
i, x) bằng sub(S, 0, i+1) + <x> + sub(S, i+1, len(S) – i-1).
Việc xóa đi một phần tử từ một dãy có thể được được đặc tả theo một trong hai
cách Nếu x là một phần tử của dãy S, S - <x> cho là dãy S mà mất đi phần tử x.
Trang 10Dãy delete(S, i) là bằng dãy S với phần tử ở vị trí i đã xóa đi delete(S, i) cũng có thể được viết trong thuật ngữ của các phép toán khác như sub( S, 0, i) + sub( S,
i+1, len(S) – i – 1).
ADT cho các xâu ký tự với chiều dài thay đổi.
Như là một sự minh họa của việc dùng ký hiệu dãy khi định nghĩa một loại
dữ liệu trừu tượng, chúng ta phát triển một sự đặc tả về một loại dữ liệu trừu tượng với xâu ký tự có chiều dài thay đổi Có bốn phép toán cơ sở (ngoại trừ phép tính bằng và phép gán) thông thường kể cả trong các hệ thống mà hỗ trợ các xâu ký tự như:
length một hàm mà trả về xâu ký tự có chiều dài hiện tại.
concat một hàm mà trả về sự nối kết của hai xâu ký tự đầu vào.
substr một hàm mà trả về một chuỗi con của một xâu ký tự đã cho.
pos một hàm mà trả về vị trí đầu tiên của một xâu ký tự như là một chuỗi con
khác
abstract typedef <<char>> STRING;
abstract length(s)
STRING s;
postcondition length == len(s);
abstract STRING concat(s1,s2)
STRING s1.s2;
postconidtion concat == s1 + s2;
abstract STRING substr(s1, i, j)
STRING s1;
int i, j;
precondition 0 <= i < len(s1);
0 <= j <= len(s1) – i;