a. Kiểu tập hợp
*. Khai báo:
Một tập hợp thì gồm nhiều giá trị có chung một kiểu dữ liệu, gọi là kiểu cơ bản. Kiểu cơ bản phải là kiểu vô hướng đếm được, tức chỉ có thể là kiểu byte, ký tự , lôgic hay liệt kê. Số phần tử của tập hợp tối đa là 256 phần tử.
Kiểu tập hợp được mô tả bằng từ khóa SET OF , kế đến là kiểu cơ bản của các phần tử.
Ví dụ:
TYPE Kky_tu = SET OF Char; Kchu_hoa = SET OF ‘A’.. ‘Z’; Kso = SET OF Byte;
Var TapA, TapB: Kky_tu; TapH: Kchu_hoa; TapS: Kso;
Các tập hợp TapA, TapB, TapH, TapS nói trên cũng có thể khai báo trực tiếp như sau:
Var TapA, TapB: SET OF Char;
TapH: SET OF ‘A’.. ‘Z’; TapS: SET OF Byte;
Chú ý rằng các khai báo dưới đây là sai, vì kiểu cơ bản không thể là Integer, và nếu là đoạn con thì phạm vi của đoạn con không được vượt quá phạm vi của kiểu Byte:
Var T1: SET OF Integer; T2: SET OF 1..256 ;
* Xác định một tập hợp
Một tập hợp được xác định bằng cách liệt kê các phần tử của nó, các phần tử được phân cách nhau bởi dấu phẩy, và đặt giữa hai dấu ngoặc vuông.
[ ]: tập rỗng
[3..6]: tập các số nguyên 4, 3, 5, 6
[3..6, 9, 12]: tập các số nguyên 3, 4, 5, 6, 9, 12
[‘A’..’C’, ‘X’, ‘Z’]: tập các chữ ‘A’, ‘B’, ‘C’, ‘X’, ‘Z’ Các phần tử của tập hợp có thể là biến hay biểu thức, ví dụ:
[3, 5, i+j, 2*j]: tập hợp này có 4 phần tử là 3, 5, hai phần tử kia có giá trị bằng i+j và 2*j, trong đó i, j là các số nguyên sao cho i+j và 2*j nằm trong phạm vi từ 0 đến 254.
* Các phép toán
Phép gán:
Có thể gán một tập hợp cho một biến tập hợp cùng kiểu. Ví dụ, với các biến khai báo ở trên, có thể gán:
TapA:=[‘1’..’5’, ‘9’, ‘A’]; TapH:=[‘C’..’F’];
TapS:=[14..20, 30, 40];
Tập rỗng [ ] gán cho biến tập hợp kiểu nào cũng được: TapA:=[ ]; TapH:= [ ];
Lệnh gán dưới đây là sai vì hai vế không cùng kiểu dữ liệu: TapA:=[1..8]; TapS:=[‘1’..’9’];
Phép hợp:
Hợp của hai tập hợp A và B, ký hiệu là A+B, là một tập hợp gồm các phần tử hoặc thuộc tập A hoặc thuộc tập B. Ví dụ:
[3..5]+[3..6,10, 15]=[ 3..6, 10, 15]
Phép giao:
Giao của hai tập hợp A và B, ký hiệu là A*B, là một tập hợp gồm các phần tử đồng thời thuộc A và B.
Ví dụ: [1..10]*[4..15] =[4..10]
Phép hiệu:
Hiệu của hai tập hợp A và B, ký hiệu là A-B, là một tập hợp gồm các phần tử thuộc tập A nhưng không thuộc tập B.
Nhận xét:Các phép toán trên chỉ thực hiện được khi A và B cùng kiểu, và kết quả là một tập hợp C cùng kiểu với A và B.
Phép thử IN (thuộc về):
Dùng để kiểm tra xem một biến hay một giá trị có phải là phần tử của một tập hợp nào đó không.
Biểu thức x IN TapA cho kết quả là True nếu giá trị x thuộc TapA, cho giá trị False trong trường hợp ngược lại.
Ví dụ:
‘N’ IN [‘N’, ‘n’] cho kết quả là TRUE ‘y’ IN [‘N’, ‘n’] cho kết quả là FALSE
Các phép so sánh (=, <>, <=, >=):
Cho A và B là hai tập hợp cùng kiểu, kết quả của các phép so sánh A với B sẽ là TRUE hoặc FALSE.
• Phép bằng: A=B khi và chỉ khi mỗi phần tử của A đều thuộc B và mỗi phần tử của B đều thuộc A, trong trường hợp ngược lại, ta nói A khác B và viết A<>B.
Ví dụ:
[3,2,4,5]= [2, 3..5] [‘A’, ‘B’]<>[‘a’,’B’]
• Phép nhỏ hơn hoặc bằng: A<=B khi và chỉ khi mọi phần tử của A đều thuộc B, ví dụ:
[3..5]<=[3..5]; [3..5]<=[1..6]; cho kết quả True
• Phép lớn hơn hoặc bằng: A>=B khi và chỉ khi mọi phần tử của B đều thuộc A, nói cách khác A>=B khi và chỉ khi B<=A.
[3..5]>=[3..5]
[‘A’..’Z’]>=[‘A’..’D’]
Chú ý rằng trong các tập hợp không có phép so sánh nhỏ hơn (<) và lớn hơn (>). Khi cần so sánh lớn hơn hay nhỏ hơn ta có thể viết:
If (A<=B) and (A<>B) then writeln(‘A < B’); If (A>=B) and (A<>B) then writeln(‘A > B’);
b. Dữ liệu kiểu tập tin
Nhập và xuất dữ liệu là hai công việc rất phổ biến khi thực hiện một chương trình. Cho đến nay, ta mới chỉ nhập dữ liệu từ bàn phím và xuất dữ liệu ra màn hình. Các dữ liệu này được tổ chức trong bộ nhớ của máy, chúng tồn tại khi chương trình đang chạy và bị xóa khi chương trình kết thúc. Muốn lưu trữ các dữ liệu lâu dài để sử dụng nhiều lần thì phải ghi chúng lên đĩa thành các tập tin.
Tập tin (file) trong Pascal là một kiểu dữ liệu có cấu trúc. Mỗi tập tin là một tập hợp các phần tử có cùng chung một kiểu dữ liệu được nhóm lại thành một dãy và được ghi trên đĩa dưới một cái tên chung. Khái niệm tập tin và mảng có những điểm rất gần nhau. Song tập tin khác mảng ở những điểm sau đây:
• Mảng được tổ chức trong bộ nhớ còn tập tin chủ yếu được tổ chức trên đĩa.
• Số phần tử của mảng được xác định ngay khi khai báo, còn số phần tử của tập tin thì không. Các tập tin được kết thúc bằng một dấu hiệu đặc biệt gọi là EOF (End Of File).
• Các phần tử của mảng được truy xuất thông qua chỉ số. Các phần tử của tập tin được truy xuất nhờ một biến trung gian chỉ điểm vào vị trí của chúng trên đĩa, gọi là con trỏ tệp. Tại mỗi thời điểm, con trỏ sẽ chỉ vào một vị trí nào đó trong tập tin, gọi là vị trí hiện thời.
Dưới đây sẽ trình bày hai loại tập tin thường gặp là tập tin có định kiểu và tập tin văn bản.
* Tập tin có định kiểu
Tập tin mà các phần tử của nó có cùng một kiểu dữ liệu gọi là tập tin có định kiểu. Kiểu dữ liệu của các phần tử của tập tin có thể là kiểu đơn giản (nguyên, thực, ký tự , lô gic, chuỗi ký tự...) hoặc kiểu có cấu trúc (mảng, bản ghi).
Cách khai báo kiểu tập tin như sau:
Type TênkiểuTtin = File of Kiểuphầntử ;
Ví dụ:
Type Ksvien = Record
Ten: String[20];
Namsinh: Integer; DTB: Real
end;
KieuT1 = File of Integer; KieuT2 = File of String[20]; KieuT3 = File of
Theo khai báo trên thì KieuT1 là tập tin có các phần tử kiểu nguyên (Integer ), KieuT2 là tập tin có các phần tử là các chuỗi ký tự (String[20] ), còn KieuT3 là tập tin có các phần tử là các bản ghi kiểu Ksvien.
Khi đã có kiểu tập tin, ta có thể khai báo các biến tập tin: Var F1: KieuT1; F2: KieuT2; F3: KieuT3;
F1, F2, F3 là các biến kiểu tập tin, một loại biến đặc biệt, không dùng để gán giá trị như các biến nguyên, thực hay chuỗi. Mỗi biến này đại diện cho một tập tin mà thông qua các biến đó ta có thể thực hiện các thao tác trên tập tin như: tạo tập tin, mở, đóng, xóa tập tin, ghi dữ liệu vào tập tin và đọc dữ liệu từ tập tin, ...
Ngoài cách khai báo các biến F1, F2, F3 thông qua việc định nghĩa các kiểu dữ liệu mới như trên, Pascal còn cho phép khai báo trực tiếp các biến tập tin như sau:
Var
TênbiếnTtin: File of Kiểuphầntử ;
Ví dụ: có thể khai báo ba biến F1, F2, F3 nói trên theo cách sau: Type Ksvien = Record Ten: String[20]; Namsinh: Integer; DTB: Real end; Var
F1: File of Integer; F2: File of String[20]; F3: File of Ksvien ;
Các thủ tục chuẩn
1) Thủ tục ASSIGN(biếntậptin, têntậptin)
Gán tên tập tin cho biến tập tin. Ở đây tên tập tin là một chuỗi, đặt trong cặp dấu nháy đơn, chuỗi ký tự này là tên thực sự của tập tin bao gồm cả đường dân đầy đủ, nếu nó không cùng nằm trong thư mục hiện thời chứa chương trình. Ví dụ:
Assign(F1, ‘DLIEU.DAT’); Assign(F3, ‘QLSV.DAT’);
Sau hai lệnh này, biến F1 đồng nhất với tập tin DLIEU.DAT, mọi thao tác trên biến F1 chính là thao tác trên tập tin DLIEU.DAT. Tương tự, biến F3 đồng nhất với tập tin QLSV.DAT
Khởi tạo tập tin mới, nếu tập tin đã có trên đĩa thì nó xóa đi và tạo mới. Ví dụ: Rewrite(F1) ; khởi tạo tập tin DLIEU.DAT
Rewrite(F3) ; khởi tạo tập tin QLSV.DAT 3) Thủ tục RESET(biếntậptin):
Mở tập tin đã có để sử dụng. Con trỏ tập tin trỏ vào phần tử đầu tiên (có số thứ tự là 0) của tập tin .
Ví dụ: Reset (F3); mở tập tin QLSV.DAT
4) Thủ tục WRITE(biếntậptin, b1, b2, ..., bN):
Tuần tự ghi vào tập tin các giá trị của các biến b1, b2, ..., bN. Các biến b1, ..., bN phải cùng kiểu dữ liệu với các phần tử của tập tin.
Ví dụ:
- Cho i, j, k là các biến kiểu Integer và i=10, j=20, k=100, khi đó lệnh: Write(F1, i, j, k ); sẽ ghi lần lượt các giá trị 10, 20, 100 vào tập tin DLIEU.DAT
-Cho khai báo: Var X: Ksvien;
Các lệnh sau gán giá trị cho X và ghi X vào tập tin QLSV.DAT: X.Ten:=’Ng Van An’;
X.Namsinh:=1980 ; X.DTB:=5.5; Write(F3, X);
Sau khi ghi X vào tập tin QLSV.DAT, con trỏ tập tin tự động dời đến vị trí của phần tử tiếp theo.
5) Thủ tục READ(biếntậptin, b1, b2, ..., bN):
Ðọc tuần tự các phần tử của tập tin từ vị trí hiện thời của con trỏ tập tin và gán cho các biến b1, b2, ..., bN. Kiểu dữ liệu của các biến b1, b2, ..., bN phải cùng kiểu với các phần tử của tập tin. Mỗi khi đọc xong một phần tử, con trỏ tập tin tự động dời đến phần tử tiếp theo.
Ví dụ: - Lệnh Read(F1, i, j); đọc hai số nguyên trong tập tin DLIEU.DAT (kể từ vị trí hiện thời) và gán cho các biến nguyên i, j .
- Lệnh Read(F3, X); đọc bản ghi hiện thời của tập tin QLSV.DAT và gán cho biến bản ghi X.
6) Thủ tục CLOSE(biếntậptin): Ðóng tập tin . 7) Thủ tục SEEK(biếntậptin, k ):
Ðặt con trỏ tập tin vào phần tử thứ k trong tập tin. Thủ tục này cho phép truy xuất trực tiếp một phần tử của tập tin mà không phải thực hiện tuần tự từ đầu tập tin.
Ví dụ: đọc phần tử thứ 10 của tập tin DLIEU.DAT và gán cho biến nguyên i rồi in giá trị của i:
Seek(F1, 10); Read(F1, i); Write(i);
8) Thủ tục ERASE(biếntậptin): Xóa tập tin trên đĩa
9) Thủ tục RENAME(biếntậptin, tênmới ): Ðổi tên tập tin .
Yêu cầu là tập tin phải đang đóng thì mới xóa hay đổi tên được.
Các hàm chuẩn:
1) Hàm EOF(biếntậptin): Cho kết quả True khi con trỏ tập tin đang ở cuối tệp, trong các trường hợp khác hàm cho giá trị False.
2) Hàm FILESIZE(biếntậptin):Cho số phần tử của tập tin. Nếu tập tin rỗng thì số phần tử bằng 0.
3) Hàm FILEPOS(biếntậptin):
Cho vị trí hiện thời của con trỏ tập tin. Phần tử đầu tiên có số thứ tự là 0. Phần tử cuối cùng có số thứ tự bằng FileSize -1.
Ví dụ: Ghi số 100 vào cuối tập tin DLIEU.DAT, dùng các lệnh: i:=100;
Seek(F1, FileSize(F1) ) ; {Ðặt trỏ vào cuối tập tin} Write(F1, i); { Ghi giá trị của i vào cuối tệp}
c. Tập tin văn bản
Trong Pascal có một kiểu tập tin đã được định nghĩa sẵn, đó là kiểu TEXT hay tập tin văn bản. Ðể khai báo F là biến tập tin văn bản ta viết: Var F: Text;
Các phần tử của tập tin văn bản là các ký tự được ghi thành từng dòng có độ dài khác nhau. Các dòng được phân cách nhờ các dấu kết thúc dòng (End of line). Ðó là hai ký tự điều khiển CR(Carriage return: nhảy về đầu dòng) và LF(Line feed: xuống dòng dưới). Ví dụ, đoạn văn bản sau:
Tap tin van ban Text 12345
Het
được chứa trong tập tin văn bản thành một dãy:
Tap tin van ban Text CR LF 12345 CR LF Het Eof
Các thủ tục Assign, Rewrite, Reset, Write, Read, Close, Erase, Rename đều dùng được cho tập tin văn bản. Ngoài ra còn có thêm thủ tục Append(biếntậptin) dùng để mở tập tin văn bản và cho phép ghi thêm dữ liệu vào cuối tập tin.
Ðối với tập tin văn bản, không thể đồng thời vừa ghi vừa đọc dữ liệu như tập tin có định kiểu.
Ðể ghi dữ liệu, trước tiên phải khởi tạo tập tin bằng lệnh Rewrite hay mở tập tin và đưa trỏ về cuối tệp bằng lệnh Append. Sau đó ghi dữ liệu vào tập tin bằng thủ tục Write hay Writeln.
Ðể đọc dữ liệu một tập tin đã có, trước tiên ta phải mở tập tin bằng lệnh Reset. Sau đó đọc dữ liệu bằng thủ tục Read hay Readln.
Nếu mở tập tin bằng Rewrite hoặc Append thì không thể đọc được bằng Read và Readln. Nếu mở tập tin bằng Reset thì không thể ghi được bằng Write hay Writeln.
Ghi dữ liệu vào tập tin văn bản
Thủ tục WRITE(biếntậptin, bt1, bt2, ..., btN): cho phép tuần tự ghi các giá trị của các biểu thức bt1, bt2, .. btN vào tập tin văn bản. Các biểu thức bt1, bt2, .. btN phải thuộc kiểu đơn giản chuẩn (nguyên, thực, ký tự, lôgic) hay kiểu chuỗi, và chúng không cần phải có kiểu giống nhau.
Ví dụ: Write(F, 3, 10:4, ‘a’:2, ‘Text’, 3.5:6:2);
sẽ ghi vào tập tin thành dãy như sau (Dấu ~ hiểu là một ký tự trắng): 3~~10~aText~~3.50 Chương trình dưới đây sẽ tạo tập tin văn bản T1.TXT:
rogram VIDU;
Var F: Text; A: Integer; B: Real; Begin
A:=100; B:=1233.5;
Assign(F, ’T1.TXT’); Rewrite(F);
Close(F) End.
Nội dung của tập tin T1.TXT là: ~~Ket qua=~~100~123.45
Như vậy, cách ghi dữ liệu vào tập tin văn bản hoàn toàn giống như khi in dữ liệu lên màn hình.
Thủ tục WRITELN cũng có công dụng như WRITE, nhưng ghi xong dữ liệu thì đưa con trỏ tập tin xuống dòng dưới. Ðặc biệt, lệnh Writeln(F); không ghi gì cả, chỉ đưa con trỏ tập tin xuống dòng.
Ðọc dữ liệu của tập tin văn bản
Thủ tục READ(biếntậptin, biến1, biến2, ..., biếnN) đọc tuần tự các giá trị từ tập tin và gán cho các biến. Các biến1, biến2, ..., biếnN phải có kiểu dữ liệu phù hợp vơí dữ liệu cần đọc tại vị trí tương ứng trong tập tin.
Ví dụ: Nếu tập tin T1.TXT có nội dung như sau: ~~Ket qua=~~100~123.45 thì để đọc lại các dữ liệu này, ta phải khai báo:
Var St:String[10]; i: Integer ; Z: Real; Và dùng các lệnh: Reset(F); Read(F, St, i, Z);
Giá trị của St, i, Z sẽ là: St=’~~Ket qua=’, i=100, Z=123.44.
Nếu khai báo St có kiểu String[9] thì sẽ bị lỗi vì sau khi đọc xong 9 ký tự đầu, máy sẽ đọc tiếp giá trị =~~100 cho biến nguyên i, nhưng vì giá trị này bắt đầu là dấu = nên không đổi ra số nguyên được.
Thủ tục READLN(biếntậptin, biến1, biến2, ..., biếnN ) đọc dữ liệu cho các biếN xong sẽ đưa trỏ tập tin xuống đầu dòng dưới.
Ðặc biệt, lệnh READLN(biếntậptin); không đọc gì cả, chỉ đưa con trỏ tập tin xuống dòng.
Chú ý
o Hàm Eof(F) cũng dùng được cho tập tin văn bản, ngoài ra còn có hàm EOLN(F)
cho kết quả là True hoặc False tùy theo con trỏ tập tin có đang ở cuối dòng hay không. Khi Eof(F)=True thì Eoln(F) cũng có giá trị là True.
Các tập tin văn bản ngầm định:
Trong Pascal có hai biến tập tin văn bản đã được khai báo sẵn là Input và Output , tức là máy đã ngầm khai báo:
Var
Input , Output: Text ;
Input thường là bàn phím còn Output thường là màn hình. Lệnh Readln(Input, x); được viết tắt thành Readln(x) ; Lệnh Writeln(Output, x); được viết tắt thành Writeln(x);
Máy in cũng là một tập tin văn bản, được ngầm khai báo với tên là LST. Ðể in các biểu thức bt1, bt2, ..., btN ra máy in, ta phải khai báo sử dụng thư viện chuẩn PRINTER, và dùng lệnh:
Write(LST, bt1, bt2, ... , btN);
So sánh tập tin văn bản với tập tin định kiểu:
Các tập tin có định kiểu cho phép vừa đọc vừa ghi và truy nhập trực tiếp vào từng phần tử gần giống như thao tác với mảng.
Các tập tin văn bản không cho phép đồng thời đọc, ghi và chỉ có thể đọc hoặc ghi tuần tự, nhưng cho phép ta có thể xem, sửa trực tiếp một cách dễ dàng bằng các hệ soạn thảo văn bản đơn giản, như NC hay chính Turbo Pascal.
CÂU HỎI ÔN TẬP Câu 1: Cho khai báo biến:
Var m, n: integer; x, y: Real; Lệnh nào sai:
a) m:= -4; b) n:= 3.5; c) x:= 6; d) y:= +10.5;
Câu 2: Ðể tính giá trị , chọn cách viết nào:
a) x:= -b/2a; b) x:= -b/2*a; c) ; d) x:= -b/2/a;
Câu 3: Biểu thức: 25 div 3 + 5/2*3 có giá trị là: a) 8.0; b) 14.5; c) 9.5; d) 14.0;
Câu 4: Cho ch là biến có kiểu Char. Lệnh nào đúng: a) ch:="a" b) ch:=65; c) ch:=chr(65); d) ch:='abcd';
Câu 5:Biến X được khai báo là kiểu integer. Lệnh nào sai:
a) X:= round(275/3); b) X:= 210 div 4; c) X:= SQRT(49); d) X:= ABS(-453);
Câu 6: Biểu thức nào sau đây có giá trị TRUE:
a) (100 > 76) and ('B' < 'A'); b) not (49.5 + 2 < 5) or (2 > 4 div 2); c) (49.5 + 2 < 5) and (2 < 4 div 2); d) 2*(3+5) < 18 div 4*4;
Câu 7: Khi chạy chương trình: Var St, St1: String; Begin St:= '123'; St1:= '456'; Write(St + St1) End; Kết quả in ra là:
a) '123456'; b) 123456; c) 579; d) Câu a), b), c) đều sai;
a) 65; b) A; c) 'A'; d) 'a';
Câu 9: Khi chạy chương trình: Var a, b, c, N: integer; Begin N:=546; a:=N div 100; b:=(N Mod 100) div 10; c:=(N Mod 100) Mod 10; Write(a+b+c) End. Kết quả in ra: a) 546; b) 5; c) 15; d) 6;
Câu 10: Trong khai báo sau còn bỏ trống . . . một chỗ, vì chưa xác định được kiểu dữ liệu của biến Max:
Var
A: Array[‘a’..’d’] of Real ; Ch: Char ;
Max: . . . ;
Muốn biến Max lưu giá trị lớn nhất của mảng A thì cần khai báo biến Max kiểu gì vào chỗ . . .:
a) Char b) Integer c) String d) Real
Câu 11: Khai báo nào đúng:
a) Var A: array[1..n,1..m] of integer;
b) Const n=2; m=3; Var A: array[1..n,1..m] of integer;