Kiểu dữ liệu có cấu trúc là gì

Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 20 Chương 2 Các kiểu dữ liệu có cấu trúc Trong chương này không trình bày chi tiết các kiểu dữ liệu có cấu trúc ñơn giản như kiểu mảng, chuỗi. Nội dung trọng tâm của chương là kiểu bản ghi (Record) có cấu trúc thay ñổi, kiểu tệp và kiểu tập hợp. Chương này bạn ñọc cần nắm ñược các vấn ñề sau:  Cách thức ñịnh nghĩa một kiểu dữ liệu mới  Khai báo biến với các kiểu dữ liệu do người lập trình tự ñịnh nghĩa  Cách sử dụng toán tử CASE khi khai báo bản ghi có cấu trúc thay ñổi  Cách thức ghi và ñọc dữ liệu cho ba loại tệp: tệp văn bản, tệp có kiểu và tệp không kiểu, chú trọng cách ghi dữ liệu kiểu số vào tệp văn bản và lấy số liệu ra ñể xử lý  Xử dụng dữ liệu kiểu tập hợp trong lập trình Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 21 1. Dữ liệu kiểu bản ghi (record) 1.1 Khái niệm cơ bản Kiểu bố trí dữ liệu thông dụng nhất mà con người nghĩ ra là bố trí dưới dạng bảng. Bảng ñược coi là một ñối tượng (ñể quản lý hoặc nghiên cứu), bảng bao gồm một số cột và một số dòng. Số cột, dòng trong bảng phụ thuộc vào phần mềm quản lý mà chúng ta sử dụng. Trong từng cột dữ liệu có tính chất giống nhau. Các phần mềm quản trị dữ liệu như Excel, Foxpro... ñều ứng dụng khái niệm bảng và Pascal cũng không phải là ngoại lệ. ðể có ñược một bảng trước hết Pascal xây dựng nên một dòng gọi là "bản ghi", tập hợp nhiều dòng sẽ cho một bảng, mỗi bảng ñược ghi vào bộ nhớ dưới dạng một tệp. Bản ghi (Record) là một cấu trúc bao gồm một số (cố ñịnh hoặc thay ñổi) các phần tử có kiểu khác nhau nhưng liên quan với nhau. Các phần tử này gọi là các trường (Field). Ví dụ bảng ñiểm của lớp học bao gồm các trường Hoten, Ngaysinh, Gioitinh, Lop, Diachi, Toan, Ly, Hoa,...., dữ liệu ñiền vào các trường hình thành nên một bản ghi (Record). Có thể có những trường trong một bản ghi lại là một bản ghi, ví dụ trường Ngaysinh ở trên có thể là một bản ghi của ba trường là Ngay, Thang, Nam. Bản ghi không phải là kiểu dữ liệu ñã có sẵn trong Pascal mà do người sử dụng tự ñịnh nghĩa do ñó chúng phải ñược khai báo ở phần TYPE. Bản ghi bao gồm hai loại: * Bản ghi có cấu trúc không ñổi : là loại bản ghi mà cấu trúc ñã ñược ñịnh nghĩa ngay từ khi khai báo và giữ nguyên trong suốt quá trình xử lý. * Bản ghi có cấu trúc thay ñổi: là loại bản ghi mà cấu trúc của nó (tên trường, số trường, kiểu trường) thay ñổi tuỳ thuộc vào những ñiều kiện cụ thể. Loại bản ghi này khi khai báo thì vẫn khai báo ñầy ñủ song khi xử lý thì số trường có thể giảm ñi (so với cấu trúc ñã khai báo) chứ không thể tăng lên. ðiểm mạnh của Bản ghi là cho phép xây dựng những cấu trúc dữ liệu ña dạng phục vụ công việc quản lý, tuy vậy muốn lưu trữ dữ liệu ñể sử dụng nhiều lần thì phải kết hợp kiểu Bản ghi với kiểu Tệp. 1.2 Khai báo Kiểu dữ liệu của các trường trong Record có thể hoàn toàn khác nhau và ñược khai báo sau tên trường, những trường có cùng kiểu dữ liệu có thể khai báo cùng trên một dòng phân cách bởi dấu phảy "," . Cuối mỗi khai báo trường phải có dấu ";" . Kiểu dữ liệu Record ñược khai báo như sau: TYPE = RECORD : Kiểu; : Kiểu; ...... : Kiểu; END; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 22 Ví dụ 2.1 Khai báo kiểu dữ liệu BANGDIEM bao gồm một số trường nhằm phục vụ việc quản lý ñiểm. TYPE BANGDIEM = RECORD Hoten: String[25]; Gioitinh: Char; Lop: String[5]; Diachi: String[30]; Toan,Ly,Hoa: Real; END; Với khai báo như trên dung lượng bộ nhớ dành cho các trường (tính bằng Byte) sẽ là: Hoten 26, Gioitinh 1, Lop 6, Diachi 31, Toan 6, Ly 6, Hoa 6. (Các trường kiểu String bao giờ cũng cần thêm 1 Byte chứa ký tự xác ñịnh ñộ dài chuỗi). Tổng ñộ dài của Record bằng 26+1+6+31+18=82 Bytes. Có thể dùng hàm Sizeof(tên kiểu) ñể xác ñịnh ñộ dài một kiểu dữ liệu, ví dụ: Write(sizeof(bangdiem)) sẽ nhận ñược số 82 Ví dụ 2.2 Xây dựng kiểu dữ liệu quản lý hồ sơ công chức. Chúng ta sẽ tạo ra bốn kiểu dữ liệu mới ñặt tên là Diadanh, Donvi, Ngay và Lylich. Type Diadanh = Record Tinh, Huyen, Xa, Thon: String[15]; End; Donvi = Record Truong: String[30]; Khoa, Bomon: String[20] End; Ngay = Record Ng: 1..31; Th: 1..12; Nam: Integer; End; Lylich = Record Mhs: Word; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 23 Hoten: String[25]; Ngaysinh: Ngay; Quequan: Diadanh; Coquan: Donvi; End; Trong cách khai báo trên trường Ngaysinh thuộc kiểu Ngay, Quequan thuộc kiểu Diadanh, Coquan thuộc kiểu Donvi, nói cách khác ba trường này lại chính là ba Record. ðể khắc phục cách khai báo nhiều kiểu bản ghi như trên có thể sử dụng các bản ghi lồng nhau. Kiểu bản ghi lồng nhau có thể khai báo trực tiếp, nghĩa là không cần khai báo riêng rẽ các bản ghi con. Ví dụ 2.3 Uses crt; Type Lylich=record Mhs:word; Hoten:string[25]; Ngaysinh:record Ng:1..31; Th:1..12; Nam:Integer; End; Quequan:record Tinh,Huyen,xa,thon:string[15]; End; Coquan:record Truong:string[30]; Khoa, Bomon:string[20]; End; End; ................. Ngoài cách khai báo kiểu rồi mới khai báo biến, Pascal cho phép khai báo trực tiếp biến kiểu bản ghi theo cú pháp sau: Var Tên biến:Record Tên trường 1:kiểu trường; Tên trường 2:kiểu trường; ................. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 24 End; 1.3 Truy nhập vào các trường của bản ghi Sau khi ñã khai báo kiểu dữ liệu ta phải khai báo biến, giả sử cần quản lý danh sách cán bộ một trường ñại học chúng ta phải khai báo một biến chứa danh sách viết tắt là DS. Khi ñó ta phải khai VAR DS: Lylich; Giống như hai kiểu dữ liệu Mảng và Chuỗi, việc xử lý ñược thực hiện trên các phần tử của mảng hoặc chuỗi. ở ñây mặc dù DS là một biến nhưng chúng ta không thể xử lý chính biến ñó mà chỉ có thể xử lý các trường của biến DS. ðể truy nhập vào trường cần viết: ..…. Ví dụ ñể nhập dữ liệu cho trường Hoten ta viết các lệnh: Write(' Ho va ten can bo: '); Readln(DS.hoten); Lệnh Readln(DS.hoten); cho phép ta gán Họ tên cán bộ vào trường Hoten của bản ghi hiện thời. ðể nhập ngày tháng năm sinh chúng ta phải truy nhập vào các trường con Readln(Ds.Ngay.Ngays); Readln(Ds.Ngay.Thang); Readln(Ds.Ngay.Nam); Lệnh viết dữ liệu ra màn hình cũng có cú pháp giống như lệnh nhập. Writeln(DS.Hoten); Writeln(Ds.Ngay.Ngays); ........... Chú ý: Khi khai báo biến DS kiểu LYLICH chúng ta có thể nhập dữ liệu vào biến DS nhưng chỉ nhập ñược một bản ghi nghĩa là chỉ nhập dữ liệu ñược cho một người. Nếu muốn có một danh sách gồm nhiều người thì phải có nhiều bản ghi, ñể thực hiện ñiều này chúng ta có thể xây dựng một mảng các bản ghi. Trình tự các bước như sau: * ðịnh nghĩa kiểu dữ liệu bản ghi * Khai báo biến mảng với số phần tử là số người cần quản lý, kiểu phần tử mảng là kiểu Bản ghi ñã ñịnh nghĩa. (xem ví dụ 2.4) Với tất cả các trường khi truy nhập ta luôn phải ghi tên biến rồi ñến tên trường mẹ, tên trường con, … ñiều này không chỉ làm mất thời gian mà còn khiến cho chương trình không ñẹp, Pascal khắc phục nhược ñiểm này bằng cách ñưa vào lệnh WITH... DO. 1.4 Lệnh WITH...DO Cú pháp của lệnh: WITH DO Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 25 Khi sử dụng lệnh WITH...DO chuỗi lệnh viết sau DO chỉ cần viết tên trường có liên quan mà không cần viết tên biến. Xét ví dụ nhập ñiểm cho lớp học với giả thiết lớp có nhiều nhất là 40 học sinh. Ví dụ 2.4: Program Nhapdiem; Uses CRT; Type BANGDIEM = RECORD Hoten: String[25]; Gioitinh: ('T','G'); (* kiểu liệt kê, 'T' = Trai, 'G' = Gái *) Lop: String[5]; Diachi: String[50]; Toan,Ly,Hoa: Real; End; Var DS_LOP: Array[1..40] of BANGDIEM (*danh sách lớp là mảng 40 phần tử*) i,j: Integer; lam: Char; BEGIN clrscr; lam:='C'; i:=1; Repeat With DS_LOP[i] Do Begin Write(' Ho va ten hoc sinh: '); Readln(Hoten); Write(' Trai hay gai T/G: '); Readln(Gioitinh); Write(' Thuoc lop: '); Readln(Lop); Write(' Cho o thuong tru: '); Readln(Diachi); Write(' Diem toan: '); Readln(Toan); Write(' Diem ly: '); Readln(Ly); Write(' Diem hoa: '); Readln(Hoa); End; i:=i+1; Write(' NHAP TIEP HAY THOI ? C/K '); Readln(lam); Until upcase(lam)='K'; clrscr; For j:=1 to i-1 do With DS_LOP[j] DO Writeln(Hoten:15,' ',Gioitinh:2,' ',Lop:4,' ',Diachi:10,' Toan:', Toan:4:2,' Ly:',Ly:4:2,' Hoa:',Hoa:4:2); Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 26 Repeat Until Keypressed; END. Ví dụ 2.4 sử dụng mảng DS_LOP gồm 40 phần tử, mỗi phần tử là một Record. Mỗi lần nhập xong dữ liệu cho một phần tử lại hỏi "Nhap tiep hay thoi?", như vậy số phần tử cụ thể của mảng tuỳ thuộc vào câu trả lời C hay là K. Vấn ñề ñáng quan tâm ở ñây là cách sử dụng lệnh With ... Do, ví dụ 2.4 sử dụng biến mảng DS_LOP vì vậy trước tiên phải truy nhập vào phần tử thứ i của mảng DS_LOP (1<=i<=40), với mỗi phần tử chúng ta tiếp tục truy nhập vào các trường của chúng. Với lệnh With DS_LOP[i] Do chúng ta có thể nhập trực tiếp dữ liệu vào các trường Hoten, Gioitinh, ... Write(' Ho va ten hoc sinh: '); Readln(Hoten); Write(' Trai hay gai T/G: '); Readln(Gioitinh); ........ ðể viết dữ liệu ra màn hình chúng ta cũng dùng lệnh With … Do With DS_LOP[j] DO Writeln(Hoten:15,' ',Gioitinh:2,' ',Lop:4,' ',Diachi:10,' Toan:', Toan:4:2,' Ly:',Ly:4:2,' Hoa:',Hoa:4:2); ðối với các bản ghi lồng nhau như ví dụ 2.3 thì lệnh With.. Do cũng phải lồng nhau, xét ví dụ sau: Ví dụ 2.5 Program Kieu_Record; Uses crt; Type Tencb=record Mahoso:word; Hoten:string[25]; Ngaysinh: Record ngay:1..31; thang:1..12; nam:integer; End; End; Var DS: array[1..50] of tencb; i,j:byte; tl:char; Begin Clrscr; i:=1; Repeat Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 27 With ds[i] do Begin Write('Ma ho so: '); Readln(mahoso); Write('Ho va ten: '); readln(hoten); With ngaysinh do Begin Write('Ngay sinh: '); readln(ngay); Write('Thang sinh: '); readln(thang); Write('Nam sinh: '); readln(nam); End; End; Write('Nhap tiep hay thoi? C/K '); readln(tl); If upcase(tl)='C' then i:=i+1; Until upcase(tl)='K'; clrscr; Writeln('DANH SACH CAN BO CO QUAN'); For j:= 1 to i do Begin With ds[j] do Begin Write(mahoso,' ',hoten,' '); With ngaysinh do Writeln(ngay,' ',thang,' ',nam); End; End; Readln; END. Trong ví dụ 2.5 ñể nhập dữ liệu vào bản ghi và viết dữ liệu (tức là danh sách cán bộ cơ quan) ra màn hình, chương trình cần phải sử dụng hai lệnh With .. Do lồng nhau, lệnh thứ nhất với biến DS, còn lệnh thứ hai với biến Ngaysinh. Tuy nhiên nếu có một chút tinh ý thì các khối chương trình nhập và viết dữ liệu ra màn hình có thể thu gọn lại, ví dụ khối viết dữ liệu ra màn hình sẽ như sau: Writeln('DANH SACH CAN BO CO QUAN'); For j:= 1 to i do With ds[j] do Writeln(mahoso,' ',hoten,' ',ngaysinh.ngay,' ',ngaysinh.thang,' ',ngaysinh.nam); 1.5 Bản ghi có cấu trúc thay ñổi Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 28 a. Xây dựng kiểu dữ liệu Bản ghi có cấu trúc cố ñịnh dùng ñể mô tả một ñối tượng mà các cá thể của nó (tức là các xuất hiện của ñối tượng) có các thuộc tính (các trường) như nhau. Ví dụ DS là bản ghi mô tả một loại ñối tượng là "học viên ", mỗi xuất hiện của DS ứng với một học viên cụ thể và tất cả các học viên ñều có các thuộc tính như nhau bao gồm (xem ví dụ 2.5) Mahoso, Hoten, Ngay, Thang, Nam. Trong thực tế nhiều khi ta gặp những ñối tượng mà thuộc tính của chúng lại gồm hai loại: - Thuộc tính chung cho mọi xuất hiện - Thuộc tính riêng cho một số xuất hiện ñặc biệt Dưới ñây là một số ví dụ minh hoạ: - Kiểu dữ liệu quản lý vé ngành ñườg sắt Trên một tuyến ñường sắt có nhiều ñoàn tàu chạy trong ngày, có những chuyến tốc hành chỉ dừng lại ở một vài ga dọc ñường, có những chuyến tàu thường dừng lại tất cả các ga lẻ. Với tàu tốc hành, hành khách chỉ ñược mang theo hành lý không quá 20 Kg và sẽ có suất ăn trên tàu. Với tàu thường hành khách phải mua vé hàng hoá nếu có vận chuyển hàng hoá và không có suất ăn trên tàu. * Thuộc tính chung: tên ñoàn tàu (TDT), tuyến ñường (TD), giờ ñi (GD), loại tàu (LT) (ví dụ: tốc hành - TH, tàu thường - TT) * Thuộc tính riêng với tàu tốc hành: Số xuất ăn (SXA), số ga lẻ dừng dọc ñường (SGD), còn tàu thường có thuộc tính riêng là cước hàng hoá (CHH). Như vậy việc xây dựng các bản ghi dữ liệu sẽ phải chú ý ñến các thuộc tính chung cho các loại tàu và các thuộc tính riêng của từng loại (xem ví dụ 2.6). Ví dụ 2.6: Type QLDS = record Ten_doan_tau: string[3]; Tuyen_duong: string[15]; Gio_di: real; Case Loai_tau : (Toc_hanh, Tau_thuong) of Toc_hanh: (So_xuat_an:Word; So_ga_do: Byte); Tau_thuong: (cuoc_hang_hoa:real); End; Ví dụ 2.6 cho ta một kiểu dữ liệu bản ghi có cấu trúc thay ñổi một mức, sự thay ñổi ở ñây thể hiện qua thuộc tính Loai_tau. Như vậy tổng số trường của mỗi bàn ghi tuỳ thuộc vào ñoàn tàu ñó thuộc loại gì. Nếu là tàu tốc hành thì mỗi bản ghi có 5 trường, còn tàu thường chỉ có 4 trường. ðộ dài của các bản ghi ñược tính căn cứ vào ñộ dài của các trường có trong bản ghi ñó. Như ñã biết ñộ dài từng trường ñược tính như sau: Ten_doan_tau: 4, Tuyen_duong: 16 Gio_di: 6 Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 29 So_xuat_an: 2 So_ga_do: 1 Ve_hang_hoa: 6 Bản ghi với tàu tốc hành sẽ là 4+16+6+2+1 = 29 Byte Bản ghi với tàu thường là: 4+16+6+6 = 32 Byte - Các bước ñịnh nghĩa kiểu bản ghi có cấu trúc thay ñổi: - ðể ñịnh nghĩa một kiểu bản ghi có cấu trúc thay ñổi, chúng ta khai báo các thuộc tính chung trước, tiếp ñó tìm trong các thuộc tính chung một thuộc tính dùng ñể phân loại. - Thuộc tính phân loại có thể bao gồm một hoặc một số chỉ tiêu phân loại, tất cả các chỉ tiêu của thuộc tính phân loại phải ñặt trong cặp dấu mở-ñóng ngoặc ñơn, ví dụ: Loai_tau : (Toc_hanh, Tau_thuong) - Sử dụng toán tử Case .. . of ñể phân loại, ví dụ: Case Loai_tau : (Toc_hanh, Tau_thuong) of Toc_hanh: ..... Tau_thuong:.... - Với mỗi chỉ tiêu phân loại, chúng ta có thể khai báo tên một số trường thay ñổi hoặc khai báo một bản ghi con với cấu trúc thay ñổi, ví dụ: Case Loai_tau : (Toc_hanh, Tau_thuong) of Toc_hanh: (So_xuat_an, So_ga_do: Word); Tau_thuong: ( Case Cuoc :(cuoc_hanh_ly,cuoc_hang_hoa) of ............ ); - Các trường thay ñổi nếu có cùng kiểu dữ liệu thì tên trường viết cách nhau bởi dấu phảy. - Dữ liệu các trường phân loại phải thuộc kiểu ñơn giản, cụ thể là: kiểu nguyên, thực, logic, chuỗi, liệt kê, khoảng con. ðể phân loại chúng ta dùng toán tử Case … Of. Cần chú ý rằng toán tử Case .. Of ở ñây không giống như cấu trúc Case .. Of ñã nêu trong phần các cấu trúc lập trình nghĩa là cuối phần khai báo không có từ khoá "End;" Trong vùng nhớ cấp phát cho chương trình sẽ có hai ñoạn dành cho hai loại trường, ñoạn thứ nhất dành cho các trường cố ñịnh, trong ví dụ 2.6 ñoạn này có dung lượng là 26 byte. ðoạn thứ hai dành cho các trường thay ñổi, ñoạn này sẽ có dung lượng bằng dung lượng của chỉ tiêu phân loại lớn nhất. Trong ví dụ 2.6 trường phân loại là Loai_tau, chỉ tiêu phân loại là toc_hanh và Tau_thuong. Với chỉ tiêu Toc_hanh, chúng ta khai báo hai trường thay ñổi là So_xuat_an và So_ga_do còn với chỉ tiêu Tau_thuong có một trường là Cuoc_hang_hoa. Như vậy dung lượng của trường thay ñổi của tàu tốc hành cần 3 byte còn tàu thường cần 6 byte, ñoạn nhớ dành cho trường thay ñổi sẽ có dung lượng 6 byte. Chương trình quản lý ñường sắt ñược thiết kế bao gồm một chương trình con lấy tên là NHAP dùng ñể nhập dữ liệu cho các ñoàn tàu, phần thân chương trình chính sẽ yêu cầu nhập số chuyến tàu trên toàn tuyến và cho hiện dữ liệu ra màn hình (xem ví dụ 2.7). Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 30 Ví dụ 2.7 Program quan_ly_duong_sat; Uses crt; Type doan_tau = record ten_doan_tau:string[3]; tuyen_duong:string[15]; gio_di:real; loai:string[10]; Case loai_tau: (toc_hanh,tau_thuong) of toc_hanh:(so_xuat_an:word;so_ga_do:byte); tau_thuong:(cuoc_hang_hoa:real); End; dt = array[1..5] of doan_tau; Var dt1:dt; n,i,j:byte;tg:doan_tau; Procedure Nhap(m:byte;var qlds:dt); Begin For i:= 1 to m do with qlds[i] do Begin Write('Loai tau: ');readln(loai); if loai ='toc hanh' then Begin write('ten doan tau: '); readln(ten_doan_tau); write('tuyen duong: '); readln(tuyen_duong); write('gio xuat phat: '); readln(gio_di); write('so xuat an: '); readln(so_xuat_an); write('so ga do doc duong: '); readln(so_ga_do); writeln; end else if loai = 'tau thuong' then Begin write('ten doan tau: '); readln(ten_doan_tau); write('tuyen duong: '); readln(tuyen_duong); write('gio xuat phat: '); readln(gio_di); write('tien cuoc hang hoa: '); readln(cuoc_hang_hoa); Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 31 writeln; End; End; End; Begin clrscr; write('co bao nhieu doan tau tren toan tuyen: '); readln(n); writeln; nhap(n,dt1); clrscr; writeln('danh sach tau chay toan tuyen'); for i:= 1 to n-1 do {sap xep du lieu tang dan theo loai tau} for j:= i+1 to n do with dt1[i] do if dt1[i].loai < dt1[i+1].loai then begin tg:=dt1[i]; dt1[i]:=dt1[j]; dt1[j]:=tg; end; Writeln(' DANH MUC CAC DOAN TAU TREN TUYEN'); for i:= 1 to n do with dt1[i] do writeln(loai:10,' ',ten_doan_tau:3,' ',tuyen_duong:10,' ',gio_di:4:2,' ',so_xuat_an:3,' ',so_ga_do:3,cuoc_hang_hoa:10:2); readln; END. - Kiểu dữ liệu quản lý ñiểm của sinh viên SV là kiểu dữ liệu bản ghi dùng ñể quản lý ñiểm của sinh viên. Các trường cố ñịnh của SV bao gồm: MHS (mã hồ sơ), HOTEN (họ và tên), NS (ngày sinh), GIOI (nam, nữ), Khoa (Sư phạm - SP, Kinh tế - KT, Cơ ñiện - CD). Các môn học tuỳ thuộc vào khoa mà sinh viên ñang theo học, giả sử chúng ta quy ñịnh khoa Sư phạm có các môn: Toán, Lý, Tin cơ bản, lập trình nâng cao, khoa Kinh tế có các môn: Kế toán máy, Marketing, khoa Cơ ñiện có các môn: Cơ học máy, Sức bền vật liệu, Hình hoạ. Tất cả sinh viên nếu là Nam thì học thêm môn Bơi lội, nếu là Nữ thì học thêm Thể dục nghệ thuật. Rõ ràng là chúng ta không thể tạo ra kiểu bản ghi cố ñịnh cho sinh viên trong toàn trường, bởi lẽ số môn học không giống nhau. Các trường MHS, HOTEN, NS là chung cho mọi sinh viên, trường KHOA và GIOI dùng ñể phân loại sinh viên từ ñó xác ñịnh các môn học. Vì rằng mỗi kiểu bản ghi chỉ có thể khai báo duy nhất một trường phân loại ngang hàng với các trường cố ñịnh nên cùng một lúc chúng ta không thể phân loại theo cả KHOA và GIOI. Giải pháp duy nhất là chọn một trường phân loại với hai chỉ tiêu phân loại ñại diện cho Khoa và Gioi, giả sử tên trường phân loại bây giờ lấy tên là MONHOC và hai chỉ tiêu phân loại là PL1 và PL2. PL1 ñại diện cho Gioi còn PL2 ñại diện cho Khoa. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 32 Các chỉ tiêu phân loại lại có thể trở thành thuộc tính phân loại với một số chỉ tiêu nào ñó mà chúng ta gọi là chỉ tiêu con chẳng hạn xem PL1 là thuộc tính phân loại với hai chỉ tiêu con là Nam và Nu, PL2 là thuộc tính phân loại với ba chỉ tiêu con là SP,KT,CD. Mỗi chỉ tiêu con bao gồm một số trường cụ thể hoặc nó lại ñược sử dụng như trường phân loại mới.... Một bản ghi kiểu SV có thể có cấu trúc thuộc một trong các dạng sau: * Sinh viên khoa Sư phạm 1/ Mhs, Hoten, Ns, Boi_loi, Toan, Ly, Tincoban, Lap_trinh_nang_cao 2/ Mhs, Hoten, Ns, The_duc, Toan, Ly, Tincoban, Lap_trinh_nang_cao * Sinh viên khoa Cơ ñiện 3/ Mhs, Hoten, Ns, Boi_loi, Co_hoc_may, Suc_ben_vat_lieu, Hinh_hoa 4 / Mhs, Hoten, Ns, The_duc, Co_hoc_may, Suc_ben_vat_lieu, Hinh_hoa * Sinh viên khoa Kinh tế 5 / Mhs, Hoten, Ns, Boi_loi, Ke_toan_may, Marketing 6 / Mhs, Hoten, Ns, The_duc, Ke_toan_may, Marketing Có thể nhận thấy rằng tên các trường phân loại không có trong cấu trúc bản ghi. Nếu chúng ta muốn trong mỗi bản ghi lại có cả tên khoa và giới tính thì phải ñưa thêm vào các trường cố ñịnh mới. Kiểu dữ liệu bản ghi SV ñược khai báo như sau: Ví dụ 2.6 Type SV = record Mhs: Byte; Hoten: String[20]; NS : Record Ngay:1..31; Thang: 1..12; Nam: Word; End; Case monhoc:(pl1,pl2) of pl1:( case gioi:(nam,nu) of Nam: (Boi_loi:real); Nu: (The_duc: real)); pl2:( case KHOA: (SP,CD,KT) of SP: (Toan, Ly, Tincb, Ltnc: Real); CD: (Co_hoc_may, Suc_ben_vat_lieu, Hinh_hoa:real); KT: (Ke_toan_may, Marketing:real)); End; Từ cách khai báo trên chúng ta rút ra một số nhận xét quan trọng sau ñây: Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 33 Nhận xét 1 Trong một kiểu record các trường cố ñịnh ñược khai báo trước, trường phân loại khai báo sau, như vậy trường phân loại phải là trường khai báo cuối cùng. Các trường thay ñổi khai báo bên trong trường phân loại. Nhận xét 2 Mỗi kiểu dữ liệu Record có cấu trúc thay ñổi chỉ ñược phép có duy nhất một trường phân loại, nghĩa là không thể có hai toán tử case .... of ngang hàng khi khai báo. Nếu chúng ta khai báo kiểu SV như trong ví dụ 2.8 sau ñây thì sẽ nhận ñược thông báo lỗi : Error in Expression Ví dụ 2.8 Type SV = record Mhs: Byte; Hoten: String[20] NS : Record Ngay:1..31; Thang: 1..12; Nam: Word; End; Case GIOI: (Nam, Nu) of Nam: Boi_loi:real; Nu:The_duc: real; Case KHOA: (SP,CD,KT) of SP: (Toan, Ly, Tin_cb, Ltnc: Real); CD: (Co_hoc_may, Suc_ben_vat_lieu, Hinh_hoa:real); KT: (Ke_toan_may, Marketing:real); End; Lỗi xuất hiện do chúng ta ñã chọn hai trường phân loại là GIOI và KHOA ngang hàng nhau. ðể khắc phục lỗi này chúng ta phải lựa chọn lại cấu trúc của Record. Thay vì có hai trường phân loại cùng cấp chúng ta chọn một trường là MONHOC, chỉ tiêu phân loại là PL1 và PL2. Lúc này tên môn học cụ thể sẽ tuỳ thuộc vào giá trị mà PL1 và PL2 có thể nhận (xem ví dụ 2.9) Nhận xét 3 Vì mỗi trường lại có thể là một bản ghi cho nên bên trong trường phân loại lại có thể chứa các trường phân loại khác, ñây là trường hợp bản ghi thay ñổi nhiều mức. Với kiểu dữ liệu SV ñã ñịnh nghĩa chúng ta xây dựng chương trình quản lý ñiểm của sinh viên như ví dụ 2.9 sau ñây. Ví dụ 2.9 Program QUAN_LY_DIEM; Uses crt; Type Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 34 SV = record Mhs: Byte; Hoten: String[20]; NS : Record Ngay:1..31; Thang: 1..12; Nam: Word; End; Case monhoc:(pl1,pl2) of pl1:( case gioi:(nam,nu) of Nam: (Boi_loi:real); Nu: (The_duc: real)); pl2:( case KHOA: (SP,CD,KT) of SP: (Toan, Ly, Tincb, Ltnc: Real); CD: (Co_hoc_may, Suc_ben_vat_lieu, Hinh_hoa:real); KT: (Ke_toan_may, Marketing:real)); End; Var Ds:Array[1..100] of sv; tg:sv; i,j,k:byte; pl,tk:string[3]; tl,gt:char; BEGIN clrscr; i:=1; writeln(' Nhap diem cho sinh vien '); Repeat With ds[i] do Begin Write('Nhap ma ho so '); readln(mhs); Write('Nhap ho va ten '); readln(hoten); With ns do Begin Write('Nhap ngay sinh '); readln(ngay); Write('Nhap thang sinh '); readln(thang); Write('Nhap nam sinh '); readln(nam); End; Write('Cho biet gioi tinh nam "T" - nu "G" '); Readln(gt); if upcase(gt)='T' then ds[i].gioi:=nam else ds[i].gioi:=nu; Case ds[i].gioi of nam: begin Write('Nhap diem mon boi loi '); Readln(boi_loi); end; nu: begin Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 35 Write('Nhap diem mon The duc '); readln(the_duc); end; End; {ket thuc Case ds[i].gioi...} Write('Nhap ten khoa '); Readln(tk); for k:= 1 to length(tk) do tk[k]:=upcase(tk[k]); { chuyển tên khoa thành chữ in} if tk='SP' then ds[i].khoa:=sp else if tk='CD' then ds[i].khoa:=cd else ds[i].khoa:=kt; Case ds[i].khoa of sp:Begin Write('Nhap diem mon Toan '); Readln(toan); Write('Nhap diem mon Ly '); Readln(ly); Write('Nhap diem mon Tin Co ban '); Readln(tincb); Write('Nhap diem mon Lap trinh nang cao '); Readln(ltnc); End; cd: Begin Write('Nhap diem mon Co hoc '); Readln(co_hoc_may); Write('Nhap diem mon Suc ben vat lieu '); Readln(suc_ben_vat_lieu); Write('Nhap diem mon Hinh hoa '); Readln(hinh_hoa); End; kt: Begin Write('Nhap diem mon Ke toan may '); Readln(ke_toan_may); Write('Nhap diem mon marketing '); Readln(marketing); End; End; {ket thuc Case..} {Sap xep du lieu tang dan theo ten Khoa} for j:=1 to i-1 do for k:=j+1 to i do if ds[j].khoa