Chuong X - Turbo Pascal

Màu nền
Font chữ
Font size
Chiều cao dòng

CHƯƠNG 10 : KIỂU BẢN GHI

Khái niệm và định nghĩa kiểu bản ghi

1. Khái niệm và định nghĩa:
Chúng ta đã học về các kiểu cấu trúc dữ liệu như mảng (Array), kiểu tập hợp (Set). Các kiểu cấu trúc dữ liệu này đều được tạo ra bằng một tập hợp các phần tử có cùng (mô tả), kiểu.Ví dụ: các phần tử của một array[1..100] of Integer là các số nguyên (Integer)...
Để tạo ra một kiểu cấu trúc dữ liệu mới với các phần tử dữ liệu có kiểu khác nhau nhưng có liên kết với nhau, người ta định nghĩa ra bản ghi hay còn gọi là Thẻ ghi, Phiếu ghi (tiếng Anh là Record). Nói một cách khác, để mô tả các kiểu khác nhau, chúng ta phải dùng cấu trúc kiểu Record. Như vậy Record là một phương tiện linh hoạt nhất để xây dựng các kiểu dữ liệu mới.
Cấu trúc dữ liệu Record được gắn liền với cấu trúc dữ liệu kiểu Tệp (File) (sẽ được trình bày ở chương sau) để lưu trữ dữ liệu. Không giống một số ngôn ngữ bậc cao khác như Fortran , ngôn ngữ Pascal còn cho phép Record có thể được mô tả và sử dụng một cách độc lập với File. 
2) Mô tả Record:
Mô tả Record được viết bằng chữ Record, theo sau là danh sách mô tả các phần dữ liệu của Record mà ta gọi là trường. Mỗi một trường có một tên trường và được dùng để chọn các phần tử dữ liệu của Record, tiếp theo là mô tả kiểu trường. Mô tả Record bao giờ cũng được kết thúc bằng chữ End (có dấu chấm phẩy để kết thúc).
Để mô tả một kiểu T có cấu trúc Record với danh sách các trường có tên là S1 , S2 ,... , Sn và có các mô tả kiểu trường tương ứng là T1 , T2 ,... , Tn , ta có thể viết tổng quát sau :
Ví dụ 1: Một địa chỉ bao gồm các dữ liệu như số nhà, tên phố, thành phố. Ta mô tả Record Dia_chi như sau :
Type
Dia_chi = Record
So_nha : Integer ;
Pho : String[20] ;
Thanh_pho : String[15] ; 
End ;
Như vậy chúng ta có 3 trường là So_nha, Pho và Thanh_pho với kiểu khác nhau (integer, string[20], string[15]) và chúng được liên kết lại với nhau để mô tả địa chỉ. 
Ví dụ 2 : Để mô tả thời gian Date, ta có 3 trường : Ngày, Tháng và Năm.
Type
Date = Record
Ngay : 1.. 31 ;
Thang : 1.. 12 ;
Nam : integer ; 
End ;
hoặc theo kiểu tiếng Anh, người ta hay dùng tháng với tên gọi tháng :
Type
Date = Record
Day : 1.. 31 ;
Month : (Jan, Fer, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov ,Dec) ;
Year : integer ;
End ;
Ví dụ 3: Để miêu tả nhân sự hay phiếu cán bộ của phòng tổ chức, ta phải dùng các trường Họ tên, Ngày sinh, Chỗ ở, Lương... Ở đây ta ví dụ với 5 trường. Giả sử ta đã có mô tảù kiểu Date và Dia_chi như ở trên.
Type
Nhan_su = Record
Ho_ten : string[30] ;
Ngay_sinh : Date ;
Gioi_tinh : (Nam, nu) ;
O_tai : Dia_chi ;
Luong : Reals ;
End ;
Thí dụ 3 cho ta thấy điểm đặc biệt là việc mô tả lồng nhau của cấu trúc dữ liệu. Trong mô tả RECORD của nhân sự, ta có thể mô tả các phần tử (các trường) của Record là một kiểu Record khác như các trường Ngày sinh và chỗ ở. Mức lương được mô tả là Real (số thực) vì có thể đếm hàng xu, hàng hào.
Ta cũng có thể viết trực tiếp mô tả trường Ngay_sinh nếu như có mô tả kiểu Date :
Type
Nhan_su = Record
Ho_ten : String[30] ;
Ngay_sinh : Record 
Ngay : 1.. 31 ;
Thang : 1.. 12 ;
Nam : integer ;
End ;
Gioi_tinh : (Nam , Nu) ;
O_tai : Dia_chi ;
Luong : Reals ;
End ;

Sử dụng Record

Để thâm nhập vào môi trường của Record , ta cần phải dùng tên biến kiểu Record, sau đó dấu chấm (.) và tên trường của Record.
Ví dụ 4: Với mô tả kiểu được nêu ở trên, ta tiếp tục khai báo tên biến và thực hiện 1 đoạn chương trình để đọc dữ liệu vào các biến như sau :
Var
Nguoi_1, Nguoi_2, Nhan_su ;
Ta thấy muốn thâm nhập vào trường Ho_ten của biến Nguoi_1, ta phải viết Nguoi_1.Ho_ten. Ở đây Nguoi_1 là tên biến kiểu nhân sự và Ho_ten là tên thường. Nguoi_1.Ho_ten là một biến kiểu String[30]. Còn Nguoi_1.O_tai. Pho cho ta thâm nhập vào trường Pho của trường O_tai của biến Nguoi_1. Trong đó O_tai có mô tả kiểu Dia_chi.
Dòng lệnh nguoi_2 := Nguoi_1 cho phép ta copy 2 biến với nhau. Thực chất dòng lệnh này là việc thâm nhập vào cả một biến kiểu Record chứ không phải là việc thâm nhập vào môi trường riêng lẻ nào đó của biến Record đó (là Nguoi_1 và Nguoi_2 ở đây).
Ta có thể dùng phép so sánh :
If Nguoi_1 = Nguoi_2 then writeln ('Cung mot nguoi!) ;
hoặc :
If Nguoi_2.Ho_ten = Nguoi_1.Ho_ten then writeln (hai nguoi trung ten nhau!)
Tuy nhiên cần lưu ý là không thể dùng các thao tác sau :
_ Viết ra màn hình hoặc đọc từ bàn phím cả một biến kiểu Record như :
_ Readln (Nguoi_1) hoặc Writeln (Nguoi_1) ;
_ So sánh các Record bằng các phép toán quan hệ như sau : < , > , <= , >=. Riêng các phép so sánh <> (khác nhau) và = (bằng nhau) thì có thể được dùng với hai biến có cùng một kiểu Record.
_ Tất cả các phép toán số học và logic.
Chúng ta cũng có thể kết hợp kiểu Record với các kiểu dữ liệu khác như lập một mảng các Record. Để miêu tả đội ngũ cán bộ của một cơ quan nào đó có số người nhiều nhất là 100 chẳng hạn, ta có thể viết :
Var
Canbo : Array[1.. 100] Of Nhan_su ;
TG : Nhan_su ; (* Ô nhớ trung gian *)
I , J : integer ;
Begin
For I := 1 to 99 do
For J := I + 1 to 100 do
If Canbo[I].Ho_ten > Canbo[J].Ho_ten then
Begin
TG := Canbo[I] ;
Canbo[I] = Canbo[J] ;
Canbo[I] := TG ;
End ;

Câu lệnh With

Qua ví dụ trước ta thấy việc thâm nhập vào các trường của một biến kiểu Record là tương đối phức tạp và có phần tẻ nhạt vì phải dùng nhiều lần tên biến (Nguoi_1 trong ví dụ này) cùng với tên các trường. Để đơn giản cách viết, Pascal đưa ra lệnh WITH... DO... xét cùng với thí dụ 5 trên.
Ví dụ :
With Nguoi_1 Do
Begin
Write (' Ho va ten nguoi_1 : ') ;
Readln(Ho_ten) ;
With Ngay_sinh Do
Begin
Write ('Ngay sinh : ') ;
Readln (Ngay) ;
Write ('Thang sinh : ') ;
Readln (Thang) ;
Write ('Nam sinh : ') ;
Readln (Nam) ;
End ;
End ;
Như vậy chúng ta còn có lồng các chỉ thị WITH... DO... vào với nhau để thâm nhập vào các trường ở sâu trong Record phức tạp như biến Nguoi_1. Nó có dạng viết :
WITH A DO
WITH B DO
...
Với A, B đều được mô tả là Record, song B là một trường của A (ở thí dụ trên B là Ngay_sinh, A là Nguoi_1). Ta có thể ghi gọn lại :
WITH A DO
WITH A, B DO
WITH B DO
Begin
...
End ;
Với thí dụ 3, ta có thể thâm nhập vào các trường Ngay_sinh trực tiếp như sau :
With Nguoi_1, Ngay_sinh Do
Begin
Ngay := 12 ;
Thang := 5 ;
Nam := 1960 ;
End ;
Hoặc với mảng Canbo[50], ta có thể viết :
With Canbo[50] Do
Begin
Ho_ten := "Le Thi Tam" ; 
End ; 

Bản ghi có cấu trúc thay đổi

Các kiểu Record được trình bày ở trên cố định vì số phần tử cũng như cấu trúc của Record là đã cố định. Bên cạnh đó , ngôn ngữ Pascal cho phép thành lập các Record có cấu trúc thay đổi được hay còn gọi là Record thay đổi. 
Trước hết , ta xét ví dụ cụ thể sau : Trong mục Nhân_su , nếu ta xét thêm đến Nghe_nghiep , thì ta sẽ thấy có nhiều trường hợp xảy ra như :
_ Cong_nhan : Cần ghi rõ ngành gì , thợ bậc mấy.
_ Ky_su : Ngành gì , trình độ thực tế.
_ Bac_sy : Chuyen khoa gì
_ Cá biệt : Không ghi gì thêm.
Như vậy , lẽ ra ta phải lập một Record có đầy đủ các trường hợp kể trên khi ta giả sử rằng một người tại một 
thời điểm nào đấy chỉ có thể có một hoàn cảnh gia đình nhất định. Điều đó hoàn toàn được nhưng Record được lập ra sẽ cồng kềnh và chiếm nhiều ô nhớ. Còn cách thứ hai là lập ra 4 kiểu Record giống nhau phần đầu nhưng chỉ khác nhau phần cuối là Nghe_nghiep sẽ có các trường tương ứng với 4 nghề khác nhau. Điều này cũng làm phức tạp và cồng kềnh chương trình ra vì ta phải dùng tới 4 kiểu Record. Nhôn ngữ Pascal cho phép lập Record có dạng sau để tiết kiệm ô nhớ và cho phép linh hoạt sử dụng :
Type
Nghe = (Cong_nhan , Ky_su , Bac_sy , Ca_biet) ;
Nganh = (Dien , Co_khi , Hoa) ;
Khoa = (Noi , Ngoai , Nhi , Phu) ;
Nhan_su = Record
Ho_ten : String[30] ;
Ngay_sinh : Date ;
Gioi_tinh : (Nam , Nu) ;
O tai : Dia_chi ;
Luong : Real ;
Case Nghe_nghiep : Nghe Of 
Cong_nhan : (NganhCN : Nganh) ;
Bac_tho : Byte ;
Ky_su : (NganhKS : Nganh) ;
TrinhDoTT : (Kem , TB , Kha , Gioi) ;
Bac_sy : (Chuyen_Khoa : Khoa) ; 
Ca_biet : ( ) ;
End ; (* Of Record *)
Var
Nguoi1 , Nguoi2 : Nhan_su ;
BEGIN
..........
With Nguoi1 Do
Begin
Ho_ten := ' Nguyen Van A ' ;
Nghe_nghiep := Cong_nhan ;
NganhCN := Dien ;
Bac_tho := 3 ;
End ;
...........
With Nguoi2 Do
Begin
Ho_ten := ' Le Thi B ' ;
Nghe_nghiep := Ky_su ;
NganhKS := Dien ;
TrinhDoTT := Kha ;
End ;
END. 
CÁC QUY TẮC SỬ DỤNG RECORD CÓ CẤU TRÚC THAY ĐỔI
Record có cấu trúc thay đổi nói chung sẽ có 2 phần :
* Phần cố định : Gồm các trường Ho_ten , Ngay_sinh , O_tai ,... Phần này là đặc điểm chung của mọi người , ai cũng có cả. Cách viết này hoàn toàn như Record bình thường. 
* Phần thay đổi : Luôn đặt sau phần cố định và chỉ cho phép có một trường thay đổi mà thôi. Nói cách khác phần thay đổi được đặt sau cùng trong danh sách các trường và nó được bắt đầu bằng câu lệnh Case. Tuy nhiên , phần thay đổi có thể lại chứa Record khác có cấu trúc thay đổi , nghĩa là ta lại có trường thay đổi thứ hai khác nằm trong một trường thay đổi. Đó là Record có nhiều mức thay đổi. 
Sau đây là một ví dụ về Record có cấu trúc thay đổi ở nhiều mức. Ví dụ này được đưa ra để dành cho những bạn đọc muốn đi sâu tham khảo thêm sau khi đã nắm vững cách dùng Record thay đổi một mức là loại thường dùng nhất.
Type 
Khoa = (Dien_tu , Toan , Co_khi) ;
Nghe = (Thu_ki , Sinh_vien) ;
Diem := 0.. 10 ;
Nhan_Cong = Record 
Ho_ten : String[30] ;
Case Cong_Viec : Nghe Of
Thu_Ky : (Ngay_sinh : date , Luong : real) ;
Sinh_vien : (K : Byte) ;
Case SV_Khoa : Khoa Of 
Dien_tu : (Mach , Vi_tinh , Anh_van : Diem) ;
Toan : (Dai_so , Giai_tich , Hinh_hoc : Diem) ;
Co_khi : (Hinh_hoa , Chi_tiet_may : Diem)) ;
End ; (* Of Record *) ;
Var 
N : Nhan_cong ;
Trong Record này có 2 mức biến dạng :
Mức 1 là : Case Cong_viec : Nghe Of...
Mức 2 là : Case SV_khoa : Khoa of...
Mức 2 đương nhiên cũng tuân theo các quy tắc xây dựng Record có cấu trúc thay đổi kể trên , do đó nó phải là trường cuối cùng của mức 1: trường có nhãn là Sinh_vien. Chính trường này có cấu trúc của một Record thay đổi nằm trong Record thay đổi khác.
Các câu lệnh sau là hợp lệ :
N.Cong_viec := Sinh_vien ;
N.Ngày_sinh. Ngay := 8 ;
N.Mach := 4 ;
N.Dai_so := 4 ;
N.SV_Khoa := Dien_tu ;
N.K := 18 ;

(Hết chương 10)

Bạn đang đọc truyện trên: Truyen2U.Pro

#pascal