Dữ liệu có cấu trúc - Phần 3: Record trong Pascal

Xin chào các bạn, Hôm nay WIKIPASCAL sẽ hướng dẫn các bạn về Dữ liệu có cấu trúc - Phần 3: Record trong Pascal trong Pascal
Du lieu co cau truc - Phan 3 : Record trong Pascal
Dữ liệu có cấu trúc - Phần 3: Record trong Pascal

I – Khái niệm và định nghĩa

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ểuRecord. 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 :



Code:
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.

Code:
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 :

Code:
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.

Code:
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 :
Code:
 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 ;


II – 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ườngO_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 :


Code:
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 ;


III – 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ụ :


Code:
 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ếnNguoi_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 :

Code:
 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 :

Code:
With Canbo[50] Do

  Begin

  Ho_ten := "Le Thi Tam" ;

  End ;


IV – 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 :


Code:
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.


Code:
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ệ :

Code:
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 ;

Nhận xét

Bài đăng phổ biến từ blog này