🔙 Quay lại trang tải sách pdf ebook Giáo trình lập trình nâng cao Ebooks Nhóm Zalo TRƯỜNG ðẠI HỌC NÔNG NGHIỆP I - HÀ NỘI BỘ MÔN CÔNG NGHỆ PHẦN MỀM TS. DƯƠNG XUÂN THÀNH Giáo trình LẬP TRÌNH NÂNG CAO ( Trên ngôn ngữ Pascal ) (Soạn theo chương trình ñã ñược Bộ GD&ðT phê chuẩn) Hà nội, 2005 Lời mở ñầu Cuốn giáo trình này ñược biên soạn theo ñúng ñề cương chi tiết môn học ñã ñược Bộ Giáo dục và ðào tạo phê chuẩn. Thời gian học môn học này là 60 tiết trong ñó có 10 tiết thực hành trên máy. Tác giả là người ñã trực tiếp giảng dạy lập trình Pascal trong nhiều năm cho sinh viên chuyên tin và sinh viên các ngành khác. ðối tượng sử dụng giáo trình là sinh viên chuyên ngành Tin học hệ ñại học chính quy, tuy nhiên giáo trình cũng có thể sử dụng như là một tài liệu tham khảo cho sinh viên chuyên Tin hệ cao ñẳng và những người muốn nghiên cứu nâng cao về lập trình. Mục ñích biên soạn cuốn giáo trình là cung cấp cho người ñọc một tài liệu ñơn giản, cô ñọng những kiến thức về lập trình nâng cao. Người ñọc có thể tự học mà không nhất thiết phải có thày hướng dẫn. Giáo trình bao gồm 6 chương và 4 phụ lục. Chương 1: Chương trình con - Thủ tục và hàm, sinh viên ñã ñược học qua trong chương trình Tin học ñại cương, do vậy ở ñây chủ yếu ñi sâu vào khái niệm tham số, cách thức mà hệ thống dành bộ nhớ cho việc lưu trữ các tham số và việc gọi chương trình con từ chương trình con khác. Chương 2: Các kiểu dữ liệu có cấu trúc, tập trung vào các kiểu dữ liệu mà sinh viên chưa ñược học như bản ghi có cấu trúc thay ñổi, tập hợp.. Chương 3: ðơn vị chương trình và thư viện chuẩn, là chương chưa ñược học ở Tin học ñại cương , ở ñây hướng dẫn cách thiết kế các ðơn vị chương trình (Unit), cách thức sử dụng các Unit và tạo lập thư viện chương trình . Chương 4: Con trỏ và cấu trúc ñộng, là một chương khó, vì nó vừa liên quan ñến quản lý bộ nhớ, vừa liên quan ñến kiến thức của môn học Cấu trúc dữ liệu và Giải thuật do vậy trong chương này ñã trình bày nhiều ví dụ ñể người ñọc tham khảo. Chương 5: Giải thuật ñệ quy, ñược trình bày “hơi dài dòng” do ñặc thù của tính ñệ quy. Bài toán Tháp Hanoi ñược mô tả khác hoàn toàn so với tất cả các sách về Pascal ñã có. Chương 6: ðồ hoạ, ngoài việc giới thiệu các thủ tục vẽ thông thường, còn dành một phần trọng tâm cho việc xử lý ảnh Bitmap. Trong chương này có sử dụng một vài ví dụ của các tác giả khác (xem phần tài liệu tham khảo) nhưng ñã ñược cải tiến ñi rất nhiều. Phụ lục 1: Bảng mã ASCII Phụ lục 2: Tóm tắt các thủ tục và hàm của Turbo Pascal 7.0 Phụ lục 3: ðịnh hướng biên dịch Phụ lục 4: Thông báo lỗi Các phụ lục ñưa ra nhằm giúp người lập trình tiện tra cứu các thủ tục, hàm và xử lý các lỗi khi Pascal thông báo lỗi trên màn hình Do phải bám sát ñề cương và sự hạn chế về số trang tác giả nên trong giáo trình chưa ñưa vào ñược phần xử lý âm thanh, lập trình hướng ñối tượng.... Việc biên soạn lần ñầu không thể tránh ñược thiếu sót, tác giả mong nhận ñược sự góp ý của bạn ñọc và ñồng nghiệp ñể lần xuất bản sau sẽ tốt hơn. Mọi góp ý xin gửi về ñịa chỉ: Bộ môn Công nghệ Phần mềm, Khoa Công nghệ Thông tin, ðại học Nông nghiệp I , Trâu quỳ, Gia lâm, Hà nội. Xin trân trọng cảm ơn. Hà nội, tháng 5 năm 2005 Ts. Dương Xuân Thành Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 2 Chương I Chương trình con - Thủ tục và hàm Khái niệm chương trình con ñã ñược trình bày trong môn học Tin học ñại cương, do vậy trong chương này chúng ta nhắc lại sơ qua một số khái niệm cũ và dành thời gian cho việc tìm hiểu sâu về tham số (tham biến và tham trị), lời gọi chương trình con, cách thức bố trí chương trình con trong thân chương trình mẹ. Sau khi học chương này bạn ñọc cần nắm ñược các nội dung chủ yếu sau: Thế nào là biến toàn cục, biến ñịa phương Các biến toàn cục và biến ñịa phương ñược bố trí ở ñâu Tầm tác dụng của từng loại biến Thứ tự xây dựng các chương trình con có ảnh hưởng thế nào ñến toàn bộ chương trình Thế nào là tính ñệ quy của chương trình con Lời gọi chương trình con thế nào là ñược phép Cách khai báo trước ñể gọi chương trình con không theo thứ tự thiết kế Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 3 1. Khái niệm về chương trình con Chương trình con trong Pascal ñược hiểu là một chương trình nằm trong lòng một chương trình khác. Chương trình con gồm hai loại: Thủ tục (Procedure) và hàm (Function). Các chương trình con ñược dùng rộng rãi khi xây dựng các chương trình lớn nhằm làm cho chương trình dễ theo dõi, dễ sửa chữa. Một ñặc ñiểm nổi bật của chương trình con là nó có tính ñệ quy nhờ thế mà nhiều bài toán sẽ ñược giải quyết dễ dàng. Khi một chương trình con ñược gọi thì các biến ñược khai báo trong chương trình con (ta gọi là biến cục bộ) sẽ ñược cấp phát bộ nhớ. Kết thúc chương trình con, các biến cục bộ ñược giải phóng, ñiều này sẽ ñược lặp lại mỗi khi chương trình con ñược gọi và nó ñồng nghĩa với việc thời gian xử lý bài toán sẽ tăng lên. Bản thân tên gọi của hai loại chương trình con ñã nói lên phần nào sự khác nhau giữa chúng. Function (Hàm) là một loại chương trình con cho kết quả là một giá trị vô hướng. Khi gọi tên Function với các tham số hợp lệ ta sẽ nhận ñược các giá trị, bởi vậy tên hàm có thể ñưa vào các biểu thức tính toán như là các toán hạng. Procedure là loại chương trình con khi thực hiện không cho ra kết quả là một giá trị, mỗi Procedure nhằm thực hiện một nhóm công việc nào ñó của chương trình mẹ, vì vậy tên của Procedure không thể ñưa vào các biểu thức tính toán. Bằng cách xây dựng các chương trình con người lập trình có thể phân mảnh chương trình cho nhiều người cùng làm dưới sự chỉ ñạo thống nhất của người chủ trì. Trong Turbo Pascal ñã có sẵn một số chương trình con, ví dụ: sin(x), sqrt(x).... là các Function, còn read(), write(), gotoxy (x1,x2)..... là các Procedure. Trong một chương trình các chương trình con ñược bố trí ngay sau phần khai báo biến. Cấu trúc tổng quát một chương trình Pascal như sau: PROGRAM tên_chương_trình; USES tên các UNIT; (*khai báo các ñơn vị chương trình cần thiết*) LABEL (*khai báo nhãn*). CONST (*Khai báo hằng*) TYPE (*ñịnh nghĩa kiểu dữ liệu mới*) VAR (*khai báo biến*) PROCEDURE Tên_CTC1 (danh sách tham số hình thức); Begin ............ (*thân thủ tục thứ nhất*). End; PROCEDURE Tên_CTC2 (danh sách tham số hình thức); Begin ................... (*thân thủ tục thứ hai*) End; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 4 FUNCTION Tên_HAM1(danh sách tham số hình thức):kiểu hàm; Begin ............... (*thân hàm thứ nhất*) End; ........... BEGIN (*bắt ñầu chương trình mẹ*) .......... END. Ghi chú: 1. Các chương trình con về nguyên tắc cũng bao gồm các phần khai báo báo như ñối với một chương trình mẹ, phần nào không cần thiết thì không khai. ðiều khác nhau cơ bản là thân chương trình con nằm giữa hai từ khoá Begin và End; (sau End là dấu ";" chứ không phải là dấu "." như trong chương trình mẹ) ngoài ra chương trình con còn có thể thêm phần khai báo các tham số hình thức, các tham số hình thức ñược ñặt trong dấu () và viết ngay sau tên chương trình con. 2. Nếu chương trình con là Function thì cuối chương trình cần có lệnh gán giá trị vào tên chương trình con. 2. Tham số trong chương trình con Các chương trình con có thể không cần tham số mà chỉ có các biến riêng (biến cục bộ). Trong trường hợp cần nhận các giá trị mà chương trình mẹ truyền cho thì chương trình con cần phải có các tham số (Parameter). Tham số ñược khai báo ngay sau tên chương trình con và ñược gọi là tham số hình thức. Những giá trị lưu trữ trong các biến toàn cục của chương trình mẹ, nếu ñược truyền cho các thủ tục hoặc hàm thông qua lời gọi tên chúng thì ñược gọi là Tham số thực. Tham số hình thức bao gồm hai loại: 2.1 Tham biến (Variabic parameter) Tham biến là những giá trị mà chương trình con nhận từ chương trình mẹ, các giá trị này có thể biến ñổi trong chương trình con và khi chương trình con kết thúc các giá trị này sẽ ñược trả về cho tham số thực. Cách khai báo tham biến: Tên chương trình con (Var tên tham biến : kiểu dữ liệu); 2.2 Tham trị (Value parameter) Những tham số truyền vào cho chương trình con xử lý nhưng khi quay về chương trình mẹ vẫn phải giữ nguyên giá trị ban ñầu thì ñược gọi là tham trị. Cách khai báo tham trị: Tên chương trình con (tên tham trị : kiểu dữ liệu); Dưới ñây là một ví dụ khai báo tham số: Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 5 PROCEDURE VIDU(x,y,z: integer; lam:boolean; var qq: char); Câu lệnh khai báo chương trình con trên ñây ñồng thời khai báo các tham số hình thức trong ñó x, y,z, lam là các tham trị, với x, y,z có kiểu integer, lam có kiểu boolean, qq là tham biến vì nó ñược viết sau từ khoá VAR. Ví dụ 1.1: Lập chương trình tìm số lớn nhất trong n số nguyên ñược nhập từ bàn phím. Program Tim_cuc_dai; Uses Crt; TYPE dayso = array[1..100] of integer; (* ðịnh nghĩa kiểu dữ liệu dayso là kiểu mảng gồm nhiều nhất là 100 phần tử*). VAR a: dayso (*khai báo biến của chương trình mẹ*) n: integer; PROCEDURE nhapso(m:integer; var x:dayso); (* Nhập dãy số cần tìm cực ñại vào mảng một chiều x[i]*) Var i : integer; (*khai báo biến cục bộ của chương trình con*) Begin writeln('Nhap day so kieu integer); For i:=1 to m Do (* m ñược truyền từ chương trình mẹ qua tham số thực n*) Begin write('a[', i , '] = '); realln (x[i]); End; End; FUNCTION Max(m: integer; b:dayso); integer; (* Hàm MAX dùng ñể tìm số lớn nhất trong dãy số ñã nhập, kiểu giá trị của hàm là kiểu integer *) VAR i,t: integer; (* Biến riêng của hàm Max *) Begin t:=b[1]; (* Gán phần thứ nhất của mảng b[i] cho biến t *) For i:=2 to m Do if t = 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 ..............................................................- 21 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 ..............................................................- 22 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 ..............................................................- 23 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 ..............................................................- 24 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 ..............................................................- 25 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 ..............................................................- 26 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 ..............................................................- 27 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 ..............................................................- 28 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 ..............................................................- 29 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 ..............................................................- 30 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 ..............................................................- 31 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 ..............................................................- 32 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 ..............................................................- 33 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 ..............................................................- 34 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'*' then writeln(f,t); Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 47 i:=i+1; Until t='*'; close(f); End; Procedure Ghithem (ten:string); Begin clrscr; assign(f,ten); append(f); writeln('Hay bo xung bai tho cua ban'); i:=1; repeat write('Cau ',i,' '); readln(t); if t<>'*' then writeln(f,t); i:=i+1; Until t='*'; close(f); end; Procedure Doc(tep:string); Begin clrscr; gotoxy(15,5); i:=6; writeln('Du lieu viet tu tep ',tep); assign(f,tep); reset(f); while not eof(f) do begin readln(f,t); gotoxy(15,i); writeln(t); i:=i+1; end; readln; end; BEGIN { Thân chương trình mẹ} Clrscr; Write('cho biet ten tep '); Readln(ten); Write('Ban Ghi hay Doc du lieu G/D '); Readln(tl1); If upcase(tl1)='G' then Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 48 Begin write('Ghi tep moi hay ghi them vao tep cu M/C '); Readln(tl2); If upcase(tl2)='M' then Ghimoi(ten) else Ghithem(ten); End Else Doc(ten); END. Trong ví dụ 2.17 có sử dụng một biến toàn cục là TEN, kiểu biến là string[12]. Khi lấy Ten làm tham số thực ñể truyền cho các chương trình con thì cần chú ý ñịnh nghĩa kiểu dữ liệu trước. Cụ thể nếu chúng ta viết dòng lệnh: Procedure Doc(tep:string); Dưới dạng mới là Procedure Doc(tep:string[12]); thì máy sẽ báo lỗi. 2.3 Tệp có kiểu Tệp có kiểu là tệp mà mọi phần tử ñều có cùng ñộ dài và cùng kiểu. Kiểu các phần tử của tệp có thể là số nguyên, thực, ký tự, chuỗi, mảng hoặc bản ghi. Cách thức khai báo biến kiểu tệp ñã trình bày trong mục II. Sự khác nhau cơ bản giữa tệp có kiểu và tệp văn bản là tệp có kiểu có thể vừa ghi vào vừa ñọc ra, còn với tệp văn bản chúng ta buộc phải kết thúc ghi bằng lệnh Close(bien_tep) thì mới có thể ñọc tệp, còn khi ñang ñọc tệp chúng ta cũng phải kết thúc ñọc và ñóng tệp thì mới có thể ghi thêm dữ liệu vào tệp. a. ðọc và ghi - Ghi lên tệp Write(bientep, bien1, bien2, ...) bien1, bien2, ... là các biến cùng kiểu với biến tệp. - ðọc tệp Read(Bientep, bien1, bien2, ....) ðọc tệp thực chất là ñọc các phần tử của tệp và gán cho các bien1, bien2,... cùng kiểu. Việc ñọc diễn ra trong bộ nhớ do ñó người sử dụng muốn biết ñược các giá trị cụ thể thì phải viết các biến ñã ñọc lên màn hình. Chú ý: Khác với tệp văn bản, việc ghi và ñọc tệp có kiểu không sử dụng các lệnh Writeln hoặc Readln nghĩa là tệp có kiểu không ghi dữ liệu thành các dòng. Các phần tử của tệp có kiểu ñược ghi liên tục trong các ô nhớ và chỉ có ký hiệu kết thúc tệp EOF. Khi chúng ta ñọc hoặc ghi xong một phần tử thì con trỏ tệp sẽ tự ñộng chuyển ñến vị trí kế tiếp. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 49 b. Truy nhập vào tệp Seek(bientep,i); i=0,1,2,..... Thủ tục Seek sẽ ñịnh vị con trỏ tại phần tử thứ i của tệp, Pascal quy ñịnh vị trí các phần tử là 0, 1, 2... như vậy phần tử thứ nhất là phần tử có vị trí 0. Lệnh Seek(bientep, 5); sẽ ñịnh vị con trỏ tệp tại phần tử thứ 6. Các tệp sau khi mở theo ngầm ñịnh con trỏ tệp luôn ñịnh vị tại phần tử ñầu tiên nghĩa là phần tử có số thứ tự là 0. Trong quá trình ñọc nếu chúng ta dùng một cấu trúc lặp ñể ñọc thì khi ñọc xong một phần tử con trỏ tệp sẽ tự ñộng chuyển ñến phần tử kế tiếp. while not eof(BT) do {Kiểm tra xem con trỏ tệp ñã nằm ở phần tử cuối chưa} begin Read(bt,i); write(i:5); end; c. Các hàm xử lý tệp *. Filesize(bientep) cho biết số phần tử có trong tệp *. FilePos(bientep) cho biết vị trí hiện thời của con trỏ tệp *. Eof(Bientep) cho giá trị True nếu con trỏ tệp ở vị trí cuối tệp (vị trí cuối tệp ñựơc hiểu là vị trí sau phần tử cuối cùng), nếu con trỏ tệp ñịnh vị tại bất kỳ phần tử nào của tệp thì hàm eof() cũng cho giá trị False. Giả sử tệp DS có 8 phần tử khi ñó hàm Filesize(DS) sẽ cho kết quả là số 8, còn thủ tục Seek(DS,Filesize(DS)) sẽ ñịnh vị con trỏ tại phần tử thứ 8 (số thứ tự của phần tử này là 7) nghĩa là cuối tệp (bởi vì phần tử ñầu tiên có số thứ tự là 0). Ví dụ 2.18 Tạo một tệp lấy tên là TEPCK.DAT ñể vừa ghi vừa sửa dữ liệu Program Tep_co_kieu; Uses crt; Var bt: file of byte; i:byte; n:real; Begin clrscr; assign(bt,'TEPCK.DAT'); rewrite(bt); for i:= 0 to 5 do write(bt,i); {ghi vào tệp năm số nguyên} reset(bt); writeln(' Du lieu luu tru trong tep TEPCK.DAT '); while not eof(BT) do {viết dữ liệu từ tệp ra màn hình} begin Read(bt,i); write(i:5); Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 50 end; writeln; seek(bt,3); {ñịnh vị con trỏ tại phần tử thứ 4} textcolor(magenta); read(bt,i); {ñọc phần tử thức 4 vào biến i} writeln('So cu trong tep o vi tri 3: ',i); i:=33; seek(bt,3); write(bt,i); {sửa phần tử thứ 4, giá trị mới là 33} seek(bt,3); read(bt,i) Writeln('So moi trong tep o vi tri 3: ', i); writeln('Vi tri hien thoi cua con tro: ',filepos(bt)); readln; close(bt); End. Nhận xét: Ví dụ 2.18 cho thấy khi ghi hoặc ñọc dữ liệu vào tệp có kiểu thì chúng ta luôn luôn phải dùng ñến các biến có cùng kiểu vói biến tệp. ðoạn chương trình i:=33; seek(bt,3); write(bt,i); dùng ñể ghi vào phần tử thứ 4 giá trị mới bằng 33. Nếu chúng ta viết lại ñoạn lệnh này với ý tưởng ghi trực tiếp giá trị 33 vào phần tử thứ 4 seek(bt,3); write(bt,33); thì Pascal sẽ báo lỗi "Variable Indentifier Expertied" nghĩa là Pascal không hiểu số 33 thuộc kiểu dữ liệu gì. Ví dụ 2.19 Lập chương trình ñể nhập ñiểm cho học sinh và ghi kết quả vào tệp có tên là DIEM.DAT. Chương trình ñịnh nghĩa một kiểu dữ liệu mới là Bangdiem (Kiểu Record) với các trường: Stt, Hoten, Diachi, Gioitinh, Lop, Toan, Ly, Hoa. Biến tệp Ds thuộc loại tệp có kiểu. Ví dụ 2.19 chỉ mới làm công việc là nhập dữ liệu vào tệp mà chưa ñọc dữ liệu ra. Program Kieutep; Uses Crt; Type Bangdiem = Record Stt: Integer; Hoten, Diachi: String[25]; Gioitinh, Lop: string[5]; Toan,Ly,Hoa: Real; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 51 End; Tepdiem = File of Bangdiem; Var DS: Tepdiem; NGUOI: Bangdiem; i,j: Integer; lam,TL: Char; Begin Assign(DS,'diem.dat'); Rewrite(DS); Nguoi.stt:=1; textbackground(white); textcolor(5); lam:='L'; TL:='C'; While lam='L' do Begin clrscr; i:=10; j:=5; gotoxy(i,j); write(' So thu tu: '); gotoxy(i,j+1); write(' Ho va ten: '); gotoxy(i,j+2); write(' Nam hay nu: '); gotoxy(i,j+3); write(' Thuoc lop: '); gotoxy(i,j+4); write(' Dia chi: '); gotoxy(i,j+5); write(' Diem Toan: '); gotoxy(i,j+6); write(' Diem Ly: '); gotoxy(i,j+7); write(' Diem Hoa: '); i:=i+15; With Nguoi do Begin Gotoxy(i,j); Readln(STT); Gotoxy(i,j+1); Readln(Hoten); Gotoxy(i,j+2); Readln(Gioitinh); Gotoxy(i,j+3); Readln(lop); Gotoxy(i,j+4); Readln(Diachi); Gotoxy(i,j+5); Readln(Toan); Gotoxy(i,j+6); Readln(Ly); Gotoxy(i,j+7); Readln(Hoa); End; Gotoxy(i,j+9); Write('Co ghi vao danh sach khong? C/K '); Readln(TL); If upcase(TL) ='C' then Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 52 Begin Write(DS,NGUOI); NGUOI.STT:= NGUOI.STT+1; End; textbackground(white); textcolor(blue); Gotoxy(28,22); Write(' NHAP TIEP HAY THOI ? L/T '); Readln(lam); lam:=upcase(lam); textbackground(white); textcolor(5); End; Close(DS); End. Ví dụ 2.20 Nhập dữ liệu quản lý ñiểm tuyển sinh trung cấp vào tệp, tính tổng ñiểm và kết quả cho từng người sau ñó ñọc dữ liệu ra màn hình Ví dụ 2.20 Program Diem_trung_cap; Uses crt; Type dong=record mhs:byte; hoten:string[12]; toan,ly,tong:real; ketqua:string[7]; end; ds = file of dong; Var nguoi:dong; dsl:ds; i,n:byte; Begin clrscr; assign(dsl,'dsl.txt'); rewrite(dsl); write('Nhap bao nhieu nguoi ? '); readln(n); for i:= 1 to n do Begin With nguoi do Begin write('Ma ho so ', i); mhs:=i;writeln; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 53 write('Ho va ten '); readln(hoten); write('Diem toan '); readln(toan); write('Diem ly '); readln(ly); tong:=toan+ly; if tong>10 then ketqua:='Do' else ketqua:='Truot'; end; write(dsl,nguoi); writeln; End; close(dsl); clrscr; reset(dsl); writeln(' Du lieu luu tru trong tep DSL.TXT '); while not eof(dsl) do with nguoi do begin read(dsl,nguoi); writeln(mhs:3,hoten:15,toan:5:2,ly:5:2,tong:7:2,ketqua:6); end; readln; End. 2.4 Tệp không kiểu Như ñã biết tệp văn bản chứa ñựng trong nó chỉ các ký tự của bảng mã, tất cả các kiểu dữ liệu khác ñều phải chuyển về kiểu này. Tệp có kiểu ñòi hỏi khắt khe về kiểu dữ liệu của biến và của tệp. Tệp không kiểu là loại tệp không quan tâm ñến kiểu dữ liệu. Sự khác nhau giữa tệp có kiểu và tệp không kiểu là ở chỗ sau khi ñã khai báo biến tệp việc mở tệp ñể ghi hoặc ñể ñọc cần chỉ rõ ñộ lớn (tính bằng Byte) của từng phần tử tệp. ðiều này có nghĩa là giữa các phần tử không có ký hiệu phân cách như thường thấy (tệp văn bản là CR và LF, tệp có kiểu là khoảng cách). a. Khai báo biến tệp ðể khai báo một biến tệp không kiểu chúng ta dùng mẫu: Var Bientep: File; Ví dụ: Var BT:file; b. Mở tệp ñể ghi - ñọc Sau khi ñã khai báo biến tệp, muốn mở tệp không kiểu ñể ghi chúng ta dùng cặp thủ tục: Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 54 Assign(bientep, tentep); Rewrite(Bientep, n); Còn mở tệp ñể ñọc là các thủ tục Assign(bientep, tentep); Reset(Bientep, n); ở ñây n là ñộ lớn tính theo Byte do người sử dụng quy ñịnh ñể ghi (hoặc ñọc) dữ liệu của từng phần tử ( Record ). Nếu chọn n=1 nghĩa là mỗi phần tử có ñộ dài 1 byte thì dữ liệu ghi vào tệp chỉ có thể thuộc kiểu Byte, char, shortint, còn nếu chọn n=2 thì dữ liệu có thể là Integer, word. Với số thực Real phải chọn n=6. Giả sử mỗi phần tử ghi vào tệp là một chuỗi dài tối ña là 10 ký tự thì phải chọn n=11 bởi vì mỗi chuỗi khi ghi vào bộ nhớ cần thêm một byte ñể chứa ký tự cho biết ñộ dài thực của chuỗi. Nếu bỏ qua tham số n thì Pascal sẽ dùng giá trị ngầm ñịnh là 128. c. ðọc và ghi tệp không ñịnh kiểu * ðọc tệp không ñịnh kiểu BlockRead(bientep, biennho, i, j); Bản thân từ khoá BlockRead cho thấy việc ñọc tệp không kiểu là ñọc từng khối (block). Khối ở ñây ñược hiểu là một vùng nhớ lưu trữ dữ liệu. Khối có thể là một biến, một chuỗi, một Record hay một mảng. - bientep: là biến tệp liên kết với tên tệp ñã chỉ ra trong thủ tục assign(...) - biennho: là một biến ñã ñược khai báo cùng kiểu với các phần tử của tệp, biến nhớ ñóng vai trò vùng nhớ ñệm ñể lưu trữ dữ liệu ñọc từ phần tử của tệp ra. Từ biến nhớ này chúng ta có thể cho dữ liệu hiện trên màn hình hay in ra máy in. - i là số phần tử quy ñịh cho mỗi lần ñọc - j là một biến kiểu Word dùng ñể ghi lại số phần tử thực sự ñã ñược ñọc. Tham số j có thể bỏ nếu không cần thiết. - Ghi tệp không ñịnh kiểu * BlockWrite(bientep, biennho, i); Thủ tục BlockWrite chỉ có 3 tham số, ý nghĩa của các tham số này cũng giống như với thủ tục BlockRead d. Truy nhập tệp không kiểu Tệp không kiểu cũng ñược truy nhập như tệp có kiểu nghĩa là cũng dùng thủ tục Seek(bientep,n) ñể truy nhập vào phần tử thứ n+1 của tệp. ðiều cần ñặc biệt lưu ý là với tệp không kiểu là mỗi lần con trỏ tệp dịch chuyển nó sẽ dịch chuyển một số byte ñúng bằng số byte ñã quy ñịnh trong lệnh Rewrite() hoặc Reset() Ví dụ 2.21 Program tep_khong_kieu1; Uses crt; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 55 Var bt:file; a:array[1..15] of byte; i,j,k:byte; n:word; Begin Clrscr; assign(bt,'tep0kieu.dat'); rewrite(bt,1); {ñộ dài từng phần tử là 1 byte} For i:= 1 to 15 do begin a[i] := i; blockwrite(bt,a[i],1); {ghi vào tệp 15 số, mỗi lần ghi 1 số} end; j:=a[2]*a[3]; for i:= 0 to 14 do begin seek(bt,i); Blockread(bt,k,1); {ñọc từng phần tử của tệp vào biến K} textcolor(red); write(k,' '); end; writeln; seek(bt,filesize(bt)-1); {chuyển con trỏ ñến phần tử cuối} blockwrite(bt,j,1); {sửa giá trị phần tử cuối} seek(bt,filesize(bt)-1); blockread(bt,k,1); {ñọc lại phần tử cuối} write(k); { viết phần tử cuối lên màn hình} close(bt); readln; end. Ví dụ 2.21 tạo một biến tệp không kiểu là BT, biến tệp này kết nối với một tệp lưu trữ dữ liệu Tep0kieu.dat. ðầu tiên chương trình ghi lên tệp 15 số nguyên (1 byte), sau ñó ñọc dữ liệu từ tệp và viết chúng ra màn hình (phần tử cuối là 15). Tiếp ñó sửa dữ liệu ở phần tử cuối và lại viết phần tử ñó ra màn hình (bây giờ là 6). Ví dụ 2.22 Chương trình dưới ñây tạo tệp không kiểu BT, các phần tử tệp là chuỗi có ñộ dài bằng 3, lệnh mở tệp ñể ghi phải quy ñịnh ñộ dài phần tử bằng 4 vì trong bộ nhớ string[3] sẽ chiếm 4 bytes. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 56 Program tep_khong_kieu2; Uses crt; Var bt:file; a:array[1..30] of string[3]; i,j:byte; k:string[3]; Begin Clrscr; assign(bt,'tep0kieu.dat'); rewrite(bt,4); { ñộ lớn của mỗi phần tử là 4 bytes} For i:= 1 to 24 do begin a[i] := chr(i+64)+chr(i+65)+chr(i+66); blockwrite(bt,a[i],1); end; reset(bt,4); for i:= 0 to 23 do begin seek(bt,i); Blockread(bt,k,1); textcolor(green); write(k,' '); end; close(bt); readln; end. Ví dụ 2.23 Nhập vào tệp các phần tử là Record và sau ñó viết chúng ra màn hình. Trong phần khai báo Record chúng ta chọn Hoten là string[15] và Diem thuộc kiểu Real như vậy ñộ dài của mỗi phần tử phải là 15+1+6=22. Program tep_khong_kieu4; Uses crt; Type hs = record hoten:string[15]; diem:real; end; Var bt:file; k,nguoi:hs; i,j:byte; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 57 Begin Clrscr; assign(bt,'tep0kieu.dat'); rewrite(bt,22); write('Nhap bao nhieu nguoi? '); readln(n); For i:= 1 to n do with nguoi do Begin write('Ho va ten : '); readln(hoten); Write('Diem tong : '); readln(diem); blockwrite(bt,nguoi,1); end; for i:= 0 to n-1 do begin seek(bt,i); Blockread(bt,k,1); textcolor(red); with k do writeln(hoten,' ',diem:5:2); end; close(bt); readln; end. ðể tìm hiểu thêm về tệp không kiểu chúng ta có thể mở Help của Pascal và xem trong ñó ví dụ về việc dùng tệp không kiểu ñể sao chép dữ liệu từ tệp này sang tệp khác. 3. Dữ liệu kiểu tập hợp 3.1 Khái niệm tập hợp a. Khái niệm tập hợp Một tập hợp bao gồm n phần tử cùng kiểu dữ liệu (0 <= n <= 255), kiểu của các phần tử phải là kiểu vô hướng ñếm ñược (nguyên, ký tự, logic, liệt kê, ñoạn con). Số thứ tự các phần tử trong tập hợp luôn nằm trong khoảng 0 - 255. b. Khai báo kiểu và gán dữ liệu vào tập hợp Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 58 ðể mô tả kiểu tập hợp, Pascal dùng từ khoá Set of tiếp ñó là kiểu dữ liệu cơ bản của các phần tử tạo nên tập hợp. Cách viết kiểu dữ liệu này phải tuân theo những quy ñịnh mà Pascal ñã thiết kế: * Với Pascal 7.0 số phần tử không quá 256 và số thứ tự của các phần tử phải tăng dần * Kiểu dữ liệu cơ bản của các phần tử của tập hợp có thể viết tường minh hoặc thông qua các giá trị cụ thể. Ví dụ : Type a = set of Char; b = set of Byte; c = set of 1..100; d = set of 'a' .. 'z'; Maytinh = set of (Compact, Mitac, IBM, CMS); Trong ví dụ trên kiểu dữ liệu của các phần tử của tập hợp a, b ñược khai báo tường minh, a là tập các ký tự, b là tập các số nguyên. Kiểu dữ liệu các phần tử của tập c và d ñược xác lập qua các giá trị viết sau SET OF, tập c sẽ là tập các số nguyên trong khoảng từ 1 ñến 100, tập d là tập các chứa các chữ cái thường còn Maytinh là tập hợp của 4 phần tử ñã liệt kê. ðể thấy rõ hơn chúng ta xét ví dụ sau: Ví dụ 2.24 Program Taphop1; Uses crt; Type mau= set of (xanh, hong, tim, vang, den, nau); sn1 = set of 0..100; sn2 = 100..200; chucai1 = set of 'a'..'z'; chucai2 = set of 'A'..'Z'; Kytu = set of Char; Var a,b:mau; b1:sn1; b2 : set of sn2; c :chucai1; d:chucai2; e:kytu; i,j,k:word; tl : set of boolean; BEGIN clrscr; i:=300; j:=160; k:=i+j; a:=[xanh.. vang]; b:=[tim,nau]; b1:=[15,255,i+j,i*j]; b2:=[1, 200, 155, 123, 145]; c:=['z','à','k']; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 59 d:=[‘2’, ‘3’,'4']; tl:=[true,false]; readln; END. Ví dụ 2.24 khai báo năm kiểu tập hợp là Mau, Sn1, Chucai1, Chucai2, Kytu còn Sn2 là kiểu dữ liệu ñoạn con. Biến a thuộc kiểu tập hợp MAU, biến B1 và B2 thuộc kiểu tập hợp số nguyên, biến c và d thuộc kiểu tập hợp chữ cái, biến e thuộc kiểu ký tự còn biến Tl thuộc kiểu boolean. Các phép gán trong ví dụ sẽ xác ñịnh một số tập hợp, chẳng hạn câu lệnh a:=[xanh, hong, den, tim]; xác ñịnh tập hợp a gồm các phần tử xanh, hong, den, tim. e:=['0'..'9', 'a'..'z']; xác ñịnh tập hợp e gồm 10 ký tự 0, 1,...9 và 26 ký tự 'a', 'b', ... 'z' Khi khai báo kiểu tập hợp cần chú ý một số ñiều sau ñây: * Thứ nhât: Thứ tự các phần tử trong tập hợp luôn theo chiều tăng dần, nếu chúng ta khai báo: sn1 = set of 200..100; hoặc chucai = set of 'z' .. 'a'; thì Pascal sẽ thông báo lỗi: Lower bound greater than upper bound. * Thứ hai: Kiểu dữ liệu của các phần tử mặc dù có thể là số nguyên song khi khai báo lại chỉ có thể là kiêủ Byte. Lệnh SN0 = set of Byte; là ñúng Các lệnh khai báo sau là sai: SN1 = set of Word; SN2 = set of Integer; SN3 = set of Shortint; Lỗi mà Pascal thông báo là: Error 23: Set base type out of Range * Thứ ba: Những gía trị mà chúng ta khai báo sau từ khoá SET OF sẽ dẫn tới một trong hai khả năng: - Nếu là khai báo kiểu liệt kê, kiểu Boolean thì ñây chính là các giá trị mà tập hợp có thể nhận. Trở lại ví dụ 2.24 các phép gán a:=[xanh, tim, luc]; tl:=[tru, fal]; sẽ bị báo lỗi vì phần tử "luc" không có trong danh sách liệt kê của màu, còn Tru và Fal không có trong kiểu boolean. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 60 - Với những kiểu còn lại (nguyên, ký tự, ñoạn con) những khai báo sau từ khoá Set of không phải là giá trị của các phần tử của tập hợp mà là thứ tự của các phần tử ñó, Kiểu của các dữ liệu ñó sẽ cho biết các phần tử của tập hợp sẽ phải thuộc kiểu gì Ví dụ: 2.25 Type sn1 = set of 0..100; {các phần tử tập hợp phải là số nguyên} kytu = set of char; {các phần tử tập hợp phải thuộc kiểu ký tự} Var a : sn1; b : kytu; ..................... Với khai báo như trong ví dụ 2.25 chúng ta không thể dùng phép gán: b := ['a', 12, 'c']; ( các phần tử của tập b phải là số ) c:= [1, 2, 100]; ( các phần tử của tập c phải là ký tự ) * Thứ tư: Không ñược gán trực tiếp các số nguyên có giá trị lớn hơn 255, ví dụ phép gán b2:=[100, 200, 256]; sẽ bị báo lỗi Error 76: Constant out of Range * Thứ tư: Các phần tử trong tập hợp có thể là hằng, biến hay biểu thức. Nếu là tập số nguyên thì giá trị các biến hay biểu thức trong phép gán có thể lớn hơn 255 Trong ví dụ 2.25 phép gán sau không bị báo lỗi i:=300; j:=160; k:=i+j; b1:=[15,255,i+j,i*j]; 3.2 Phân loại tập hợp a. Tập hợp cùng kiểu Các tập hợp ñược coi là cùng kiểu nếu chúng ñược xây dựng trên cùng một kiểu cơ bản. Giả sử chúng ta khai báo các tập hợp như v í d ụ sau: Ví dụ 2.26 Type mau= set of (xanh, hong, tim, vang, den, nau); mau_xe = set of (xanh..vang); sn1 = set of 0..100; sn2 = 100..200; sn3 = set of Byte; chucai1 = set of 'a'..'z'; chucai2 = set of 'A'..'Z'; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 61 Kytu = set of Char; Var a:sn1; b:sn2; c:sn3; mau_oto:mau; chu:kytu; ....... thì các nhóm tập hợp sau ñ ây là cùng kiểu mau, mau_xe sn1, sn2, sn3 chucai1, kytu chucai2, kytu b. Hình thành một tập hợp Một tập hợp ñược hình thành khi chúng ta gán cho biến tập hợp các giá trị cụ thể, các giá trị này ñược ñặt trong hai dấu ngoặc vuông. Ví dụ 2.27 c := [2, 15, 30, 100..200]; mau_oto := [xanh, den, vang, tim]; a := []; chu := ['A'..'Z']; Nếu một tập hợp không chứa một phần tử nào cả thì nó ñược gọi là tập rỗng và ñược ký hiệu là []. Trong ví dụ 2.28, a là một biến tập hợp rỗng. Tập hợp rỗng ñược xem là cùng kiểu với mọi kiểu tập hợp. 3.3 Các phép tính trên tập hợp Các phép tính giới thiệu sau ñây chỉ thực hiện ñược trên các tập hợp cùng kiểu, nghĩa là các phần tử của mọi tập hợp phải cùng thuộc một kiểu cơ bản. a. Phép gán Phép gán ñược dùng ñể hình thành nên một tập hợp. Có thể gán một tập hợp cho một biến tập hợp cùng kiểu hoặc gán một biến tập hợp cho một biến tập khác cùng kiểu. (xem ví dụ 2.28) b. Phép hợp Phép hợp ñược ký hiệu bởi toán tử " + ". Hợp của hai tập hợp A và B khi ñó có thể viết là A + B Hợp của hai tập A và B là một tập hợp gồm các phần tử thuộc A hoặc thuộc B. Giả sử các tập A, B, C ñược hình thành như sau: A := [1..8,10..15]; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 62 B := [5..9, 20,30]; C := A + B; Tập C ñược hình thành bởi phép hợp A và B nên sẽ gồm các phần tử [1..15,20,30] c. Phép giao Giao của hai tậ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 cả A và B. C := A*B; tập C sẽ có các phần tử [5, 6, 7, 8] d. Phép trừ Phép trừ hai tập hợp A và B ký hiệu là A - B cho ta một tập gồm các phần tử thuộc A nhưng không thuộc B C := A - B; tập C sẽ có các phần tử [1..4, 10..15] còn [1..5] - [1..10] = [ ] e. Phép thuộc về Phép thuộc về ký hiệu là IN là một phép thử xem một giá trị hay một biến (không phải là biến tập) có thuộc về một tập khác hay không? Nếu có giá trị phép thử là True, ngược lại phép thử cho kết quả False. Ví dụ 2.28 T1:= ['a'..'z']; T2:= [true,false]; T3 := 'c'; Write( T3 in T1); cho kết quả True Write(false in T2); cho kết quả True Wite('1' in T1 ); cho kết quả False 3.4. Các phép so sánh trên tập Các phép so sánh nói trong mục này chỉ ñược thực hiện trên các tập cùng kiểu. Kết quả của phép so sánh thuộc kiểu Boolean nghĩa là cho ra một trong hai giá trị True hoặc False. a. Phép bằng, ký hiệu là = Hai tập A và B gọi là bằng nhau nếu 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 không phụ thuộc vào thứ tự các phần tử. Ví dụ phép so sánh [1..6] = [1,2,4,5,3,6] cho kết quả True ['a', 'b', 'c'] = ['A', 'B', 'C'] cho kết quả False b. Phép không bằng, ký hiệu là <> Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 63 Hai tập A và B gọi là không bằng nhau nếu tập A có ít nhất một phần tử không thuộc tập B hoặc tập B có ít nhất một phần tử không thuộc tập A. [1..6] <> [1,2,4,5,3,6] cho kết quả False ['a', 'b', 'c'] <> ['A', 'B', 'C'] cho kết quả True c. Phép nhỏ hơn hoặc bằng, ký hiệu là <= Tập A gọi là nhỏ hơn hoặc bằng tập B nếu mọi phần tử của tập A ñều thuộc tập B. [1..6] <= [1,2,4,5,3,6,8,9] cho kết quả True [1..6] <= [1,2,4,5,3] cho kết quả False d. Phép lớn hơn hoặc bằng, ký hiệu là >= Tập A gọi là lớn hơn hoặc bằng tập B nếu mọi phần tử của tập B ñều thuộc tập A. [1..6] >= [1,2,4,5] cho kết quả True [1,5] >= [1,2,4,5] cho kết quả False Chú ý: Trong Pascal không tồn tại các phép so sánh nhỏ hơn < và lớn hơn > Các ví dụ về dữ liệu kiểu tập hợp Ví dụ 2.29 : Tìm số nguyên tố Ta ñã biết số nguyên tố là các số chỉ chia hết cho 1 và chính nó (ngoại trừ số 1) ñể tìm các số nguyên tố trong khoảng từ 1 ñến n chúng ta sử dụng nguyên lý sàng Eratosthène. Nguyên lý này có thể phát biểu như sau: nếu a là một số nguyên tố trong tập [1..n] thì không thể tồn tại một số b thuộc tập [1..n] mà b là bội số của a. Nói cách khác nếu ta ñã tìm ñược a là một số nguyên tố thì ta phải loại trừ khỏi tập [1..n] tất cả các số là bội số của a. Program Tim_so_nguyen_to; Uses crt; Const n=255; Type Songuyen = 1..n; {ñịnh nghĩa kiểu dữ liệu Songuyen gồm n số} Var snt,sang: set of songuyen; so:songuyen; i:integer; Begin clrscr; Writeln('Cac so nguyen to tu 2 den ',n); snt:=[];{Tập snt ban ñầu là tập rỗng} sang:=[2..n]; {Xác lập tập Sang gồm số nguyên tố ñầu tiên (2) và các số khác} so:=2; {Gán vào biến So số nguyên tố ñầu tiên = 2} Repeat While not (so in sang) do so:=so+1; snt:=snt+[so]; {Bổ xung số nguyên tố vừa tìm ñược vào tập Snt} Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 64 i:=so; Write(so,' '); {Viết số nguyên tố vừa tìm ñược ra màn hình} While i<=n do Begin sang:=sang-[i]; i:=i+so; {Xoá các số là bội số của số nguyên tố vừa tìm ñược} End; Until sang = []; Readln; End. Ví dụ 2.30 Trò chơi gieo xúc xắc: nguyên tắc chơi như sau: người chơi chọn một số trong khoảng từ 1 ñến 6, máy sẽ gieo 3 con xúc xắc ñể ñược 3 số. Nếu số của người chơi nằm trong tập số mà máy ñã gieo thì người chơi thắng, ngược lại người chơi thua. Nếu ba lần chơi mà người chơi ñều thắng thì tuyên bố người chơi trúng số ñộc ñắc. Program xuc_xac; Uses crt; Var xucxac: set of 1..6; So,i,dem,a,b,c:1..6;tl:char; Begin Clrscr; Repeat clrscr; For i:= 1 to 3 do Begin Textcolor(5); Write('Moi ban chon mot so tu 1 den 6 '); readln(so); a:=random(6); b:=random(6); c:=random(6); xucxac:=[a,b,c]; Writeln('So ma ban da chon ',so); Writeln('So ma may da gieo ',a,' ',b,' ',c); If so in xucxac then Begin textcolor(14); writeln('Ban thang van thu ',i); Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 65 dem:=dem+1; End else Writeln('Ban thua van thu ',i); If dem = 3 then Writeln('Ban trung doc dac - Xin chuc mung'); End; Writeln; Write('Choi tiep hay thoi? C/K '); Readln(tl); Until upcase(tl)='K'; End. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 66 Bài tập ứng dụng chương 2 1. Nhập vào thư viện n ñầu sách (n<=50), các thông số cần ñưa vào là: Tên sách, Tên tác giả, Năm xuất bản, Tên nhà xuất bản, Số trang, Giá tiền. Hiện danh mục sách ñã nhập lên màn hình. 2. Dữ liệu tuyển sinh gồm các trường: SBD, HOTEN, TOAN, LY, HOA, TONG, KETQUA. Lập chương trình nhập dữ liệu cho một phòng thi không quá 40 thí sinh, số liệu trong trường Tong = Toan+Ly+Hoa, trường Ketqua ñiền vào chữ "DO" nếu tổng >=20, còn lại là "TRUOT". Hiện lên màn hình những người DO 3. Dữ liệu quản lý hàng hoá bao gồm: Mã hàng, Tên hàng, Số lượng, ðơn giá, Ngày nhập, Số chứng từ. Lập chương trình nhập hàng vào kho, sau mỗi mặt hàng nhập lại hỏi: "Nhap tiep hay thoi? C/K ". Nếu bấm "K" thì kết thúc nhập và hiện danh mục hàng ñã nhập lên màn hình. 4. TOADO là kiểu bản ghi chứa toạ ñộ (cột,dòng) của ba ñiểm trên màn hình. Lập chương trình kiểm tra xem ba ñiểm này có thẳng hàng không, nếu không thẳng hàng thì tạo nên tam giác gì (Vuông, Cân, ...) 5. Dữ liệu quản lý vé xe lửa gồm: Số toa, số ghế, giá vé, ga ñi, ga ñến, ðã bán vé chưa. Mỗi toa tầu có 50 ghế ñánh số thứ tự từ 1 ñến 50. Lập chương trình nhập vào những ghế ñã bán vé, thông báo còn bao nhiêu chỗ chưa bán vé. 6. Dữ liệu quản lý hàng hoá bao gồm: Mã hàng, Tên hàng, Số lượng, ðơn giá, Ngày nhập, Số chứng từ. Ngày nhập bao gồm Ngay, Thang, Nam. Lập chương trình nhập hàng vào kho, kết thúc nhập khi mã hàng bằng dấu "*" . Cho một tháng nào ñó, hiện những hàng ñã nhập trong tháng ñó. 7. Dữ liệu quản lý ñất ñai bao gồm hai trường Chủ ñất và ðất. Chủ ñất bao gồm: Mã hồ sơ, Họ tên, Mã ñất, ðịa chỉ. ðất bao gồm: Mã ñất, Diện tích, Loại ñất, Mục ñích sử dụng. Nếu mục ñích sử dụng là trồng trọt thì có thuế nông nghiệp Nếu mục ñích sử dụng là công ích (trường học, bệnh viện, sân vận ñộng) thì không phải nộp thuế. Nếu mục ñích sử dụng là kinh doanh bất ñộng sản thì có thuế ñất và thuế môn bài. Nhập dữ liệu cho n người (n<=30) sau ñó hiện lên màn hình 10 người sử dụng nhiều ñất nhất. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 67 8. Viết chương trình nhập vào tệp văn bản F một số dòng văn bản, ñếm xem trong tệp các ký tự a,b,....z mỗi ký tự xuất hiên bao nhiêu lần? 9. Thông tin về thí sinh thi ñại học bao gồm Hoten, namsinh, diemthi, ketqua. Thông tin trên ñược ghi trong một tệp văn bản, mỗi trường trên một dòng. Lập chương trình in ra màn hình mỗi thí sinh trên một dòng Ví dụ: Trong tệp In ra màn hình Tran Tam 1980 8 Kha Tran Tam 1980 8 Kha 10. Dữ liệu quản lý hàng hoá bao gồm: Mã hàng, Tên hàng, Số lượng, ðơn giá, Ngày nhập, Số hoá ñơn. Lập chương trình nhập dữ liệu từ bàn phím và ghi vào tệp có kiểu. Cho biết một số hoá ñơn, hiện lên màn hình số liệu ứng với hoá ñơn ñó. 11. T1 và T2 là các tệp văn bản, nội dung T1, T2 nhập từ bàn phím, hãy nối T1 với T2 thành T3, hiện T3 lên màn hình. 12. T1 là tệp văn bản chứa 15 ký tự dạng số Integer. Lập chương trình chuyển T1 thành tệp số nguyên T2, tính tổng các số và viết kết quả lên màn hình. 13. Giả sử thư mục hiện hành là C:\TP\BIN. Cho biết một tên tệp từ bàn phím, kiểm tra xem trong thư mục hiện hành có tệp ñó chưa, nếu có thì hiện nội dung tệp, nếu chưa thì mở tệp mới ñể ghi dữ liệu. 14. Hai tập A và B gọi là liên thông nếu chúng có ít nhất một phần tử chung. Nếu các cặp tập hợp sau là liên thông (A,B), (B,C), (C,D), (D,E) thì ta nói có một ñường nối từ A ñến E. Lập chương trình tìm ñường nối giữa n ñiểm A1, A2, ... An trên màn hình biết rằng toạ ñộ giữa các các ñiểm lần lượt là (x1, y1), (x2, y2), ... (xn, yn). In ra tất cả ñường nối theo mẫu: A1 -> A2 ->... -> An Nếu giữa các cặp ñiểm Ai, Aj không có ñường nối thì trả lời không có. Ví dụ: A1(1,2) , A2(2,3) ,A3(3,4), A4(4,1), A5(5,6) A1 -> A5 không có ñường nối A1 -> A2 có hai ñường nối A1 -> A2 A1 -> A4 -> A3 ->A2 Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 68 Chương 3 ðơn vị chương và trình Thư viện chuẩn Khái niệm ðơn vị chương trình giống như một cuốn sách và Thư viện chuẩn giống như một tủ sách. Thư viện chuẩn của Pascal không chứa hết mọi ñơn vị chương trình, lý do là vì tiết kiệm bộ nhớ. Chương này bạn ñọc nên dành nhiều thời gian cho việc khai thác các Unit ñã thiết kế trong Pascal, tuy nhiên ñã là một người lập trình thì không thể không biết tự tạo ra các Unit. Những phần người ñọc cần nắm ñược là: Các thủ tục hoặc hàm có trong các Unit System,Graph, Crt, Dos.. Phương pháp xây dựng các Unit Những nguyên tắc cần tuân thủ khi tham chiếu ñến các Unit Biên dịch Unit từ chương trình nguồn sang dạng mã máy (ñuôi tệp là TPU) Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 69 1. Khái niệm ñơn vị chương trình (Unit) Thuật ngữ Unit trong ngôn ngữ lập trình Pascal ñược dịch là "ðơn vị chương trình". Mỗi Unit ñược xem như một modul nhỏ chứa ñựng một số công cụ cần thiết giúp cho người lập trình có thể dễ dàng thiết kế chương trình. Các Unit có thể gộp chung lại thành thư viện chương trình hoặc ñể phân tán trong một thư mục quy ước của Pascal . Lệnh tham chiếu ñến Unit ñược ñặt ở ñầu chương trình với cú pháp: USES tênUnit; Ví dụ: USES CRT, GRAPH; Các Unit ñược tổ chức trong Pascal dưới hai dạng: * Các file ñộc lập với phần mở rộng là TPU (Turbo Pascal Unit), ví dụ GRAPH.TPU * File thư viện chuẩn với phần mở rộng TPL (Turbo Pascal Library), ví dụ TURBO.TPL Thư viện chuẩn của Pascal chứa ñựng một số Unit cơ bản, hay ñược dùng ñến, chúng ñược ñóng gói lại và ñược ñể cùng chỗ với tệp khởi ñộng TURBO.EXE. Thông thường chương trình Pascal ñược cài ñặt trên ổ ñĩa C, với Pascal 7.0 ñường dẫn ñến nơi lưu trữ các tệp TURBO.EXE và TURBO.TPL là: C:\TP\BIN Khi tệp TURBO ñược gọi, nghĩa là chương trình Pascal ñược khởi ñộng thì tệp TURBO.TPL cũng tự ñộng ñược tải vào bộ nhớ. Lúc này các Unit chứa trong thư viện chuẩn sẽ sẵn sàng ñược tham chiếu ñến. Việc truy cập ñến các Unit trong thư viện chuẩn nhanh hơn so với truy cập vào các Unit ñộc lập vì chúng ñã thường trú trong bộ nhớ. Khi có lời gọi một Unit nào ñó bao giờ Pascal cũng ưu tiên tìm kiếm chúng trong thư viện chuẩn, nếu không tìm thấy thì tiếp tục tìm kiếm ở bên ngoài. 2. Thư viện chuẩn Thư viện chuẩn của Pascal có tên là TURBO.TPL, thư viện này ngầm ñịnh ban ñầu chứa năm Unit là: 2.1 Crt CRT bao gồm các thủ tục quản lý màn hình, bàn phím và âm thanh, nhờ có Unit này mà người lập trình có thể thiết kế giao diện chương trình tương ñối ñẹp 2.2 Dos Unit này chứa các chức năng quản lý tệp, ñĩa, ngày tháng. Ngoài ra cũng có thể dùng Unit này ñể gọi trực tiếp các lệnh của hệ ñiều hành DOS hoặc các ngắt hệ thống. 2.3 Overlay Unit này ñược sử dụng khi chương trình nguồn có dung lượng lớn. Mặc dù ñã ñược biên dịch sang dạng mã máy song ñôi khi do bộ nhớ trong (RAM) không ñủ nên không thể tải cùng một lúc toàn bộ Code của chương trình nguồn. Sử dụng OVERLAY có thể tải từng phần Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 70 của chương trình nguồn vào bộ nhớ trong ñể chạy. Với những máy PC có Ram 128MB, hoặc 256 MB thì không mấy khi phải sử dụng Unit này. 2.4 Printer Unit này ñịnh nghĩa tên máy in là LST. Việc kết xuất thông tin bằng lệnh Write khi tham chiếu ñến LST sẽ cho phép ta in ra máy in các kết quả bài toán. 2.5 System ðây là Unit cơ bản và quan trọng nhất của Pascal, nó chứa các thủ tục vào ra như Read, Write…, các hàm sơ cấp thông dụng như Ln, Sqrt, Sin, Cos… Khi Pascal ñược khởi ñộng và thư viện chuẩn ñược nạp vào bộ nhớ thì Unit này cũng tự ñộng liên kết với mọi chương trình vì thế ở ñầu chương trình không cần ñến lời gọi USES SYSTEM. Ngoài những Unit có sẵn trong thư viện chuẩn, Pascal còn cho phép người lập trình thiết kế thêm các Unit mới, các Unit này sau ñó sẽ ñược lưu vào tệp Turbo.tpl và ñược xem như một bộ phận cấu thành của ngôn ngữ. Việc chuyển các Unit của người lập trình vào thư viện chuẩn ñược thực hiện thông qua trình tiện ích TPUMOVER sẽ ñề cập ñến ở mục sau. 3. Các Unit khác Pascal có khoảng 20 Unit ñộc lập lưu trữ trong thư mục TP\UNIT. Các Unit này ñược phân chia thành 4 nhóm gồm: * Interface Unit: nhóm Unit chung cho mọi ứng dụng * Objects Unit: danh mục (theo vần a,b,c..) của các Unit thuộc kiểu Turbo vision * System Unit: Unit hệ thống * Turbo vision Unit: Unit phục vụ cho lập trình hướng ñối tượng Muốn tìm hiểu về một Unit nào ñó ta có thể ñọc phần giới thiệu Unit trong các tệp cùng tên với phần mở rộng là INT, các tệp này lưu trữ trong thư mục TP\DOC. Các tệp ñuôi INT này là tệp văn bản nên có thể ñọc chúng bằng bất kỳ phần mềm soạn thảo văn bản nào ví dụ Word, NC… Cũng xin lưu ý thêm là một số Unit mặc dù có tên trong thư mục TP\UNIT như GRAPH3.TPU, TURBO3.TPU nhưng do ñã quá lỗi thời nên không ñược giới thiệu trong thư mục TP\DOC. Dưới ñây là một vài Unit thông dụng 3.1 Graph Unit GRAPH ñược lưu trữ trong một File riêng có tên là GRAPH.TPU. Unit này chứa các thủ tục và hàm cho phép lựa chọn loại màn hình, số dòng, cột và các thủ tục vẽ các hình cơ bản, tô màu nét vẽ hoặc màu nền các hình khép kín… 3.2 Memory Unit Memory chứa các phục vụ quản lý vùng nhớ cấp phát cho chương trình, các chương trình với phần mở rộng EXE thì vùng nhớ cơ sở ñược cấp phát là 640 Kb. ðoạn thấp nhất của vùng nhớ này (256 byte) dành cho việc ñánh ñịa chỉ các biến, hàm, thủ tục trong chương trình. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 71 3.3 Menus Unit Menus chứa các phục vụ thiết kế thực ñơn như chọn phím nóng (Hot key), tạo dải thực ñơn (Menu Bar), khung thực ñơn (menu box)… 3.4 Views Unit Views chứa các thủ tục và hàm liên quan ñến việc quan sát các khung hoặc cửa sổ như thanh cuốn (Scroll Bar), con trỏ (Cursor), chuyển trang (Nextview, Prevview)… 4. Xây dựng các Unit Các Unit cũng ñược viết và lưu vào thư mục ngầm ñịnh TP\BIN như các chương trình Pascal khác. Tệp lệnh nguồn của Unit sẽ có phần mở rộng là PAS. ðể soạn thảo một Unit chúng ta có thể dùng một phần mềm soạn thảo văn bản bất kỳ nhưng nói chung người ta vẫn hay dùng công cụ soạn thảo (Text Editor) của Pascal. Mặc dù ñược lưu dưới dạng một tệp ñuôi PAS nhưng các Unit không thể chạy bằng tổ hợp lệnh Ctrl – F9 như các chương trình bình thường. Các chương trình Pascal khi biên dịch sang dạng mã máy sẽ có phần mở rộng là EXE (Execute), còn các Unit khi biên dịch sẽ có phần mở rộng là TPU (Turbo Pascal Unit). Cấu trúc một Unit bao gồm bốn phần cơ bản sau ñây: 4.1 Phần tiêu ñề Phần này chỉ gồm một dòng mở ñầu là từ khoá UNIT sau ñó là tên Unit, kết thúc dòng bằng dấu chấm phảy. Unit tenUnit; Tên Unit phải viết cách từ khoá Unit ít nhất một khoảng trống, các ký tự của tên phải viết liền nhau theo các quy ñịnh giống như tên chương trình. Cần phải ñặc biệt lưu ý rằng Pascal quy ñịnh bắt buộc tên Unit sau này sẽ ñược dùng làm tên tệp ñể lưu trữ tệp nguồn của Unit (ñuôi Pas) ñồng thời cũng là tên của ñơn vị chương trình (ñuôi Tpu) do vậy khi ñặt tên không nên chọn tên quá dài, nên chọn các tên gợi nhớ ñến nội dung của Unit ñể tiện sử dụng về sau. 4.2 Phần khai báo chung Phần này bắt ñầu bằng từ khoá INTERFACE Bản thân từ khoá Interface có nghĩa là dùng chung, ñiều này quy ước rằng ở ñây chúng ta phải khai báo các kiểu dữ liệu mới, các biến, hằng, hàm, thủ tục mà sau này các chương trình tham chiếu ñến Unit sẽ sử dụng. Có thể hình dung rằng những gì viết trong phần này sẽ là những tài nguyên mà Unit có thể cung cấp cho các chương trình ngoài, nó giống như bản thực ñơn mà nhà hàng trình bày cho thực khách. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 72 Ví dụ chúng ta xây dựng một Unit lấy tên là HHP (hình học phẳng) trong ñó có các hàm tính diện tích , chu vi các hình chữ nhật, tam giác, tròn là dtcn, dttg, dttr, cvcn, cvtg, cvtr. Khi ñó phần khai báo chung sẽ là: INTERFACE Function dtcn( a,b: real) : real; Function dttg( a,b,c: real) : real; Function dttr( a: real) : real; Function cvcn( a,b: real) : real; Function cvtg( a,b,c: real) : real; Function cvtr( a: real) : real; Ví dụ trên khai báo ba hàm tính diên tích và ba hàm tính chu vi, sau này khi các chương trình tính toán tham chiếu ñến Unit hhp thông qua lệnh USES HHP sẽ có thể dùng trực tiếp các hàm này. Các tham số khai báo trong cả sáu hàm ñều là các tham trị nghĩa là giá trị của chúng sẽ không biến ñổi khi quay về làm việc với chương trình chính. Việc sử dụng tham số là tham trị hay tham biến sẽ tuỳ thuộc vào các bài toán cụ thể. 4.3 Phần nội dung Phần nội dung bắt ñầu bằng từ khoá IMPLEMENTATION. Tại ñây chúng ta sẽ xây dựng nên các hàm, thủ tục mà tên của chúng ñã ñược giới thiệu ở phần Interface. Việc xây dựng các chương trình con ñã ñược giới thiệu ở phần lập trình cơ bản nên chúng ta không ñề cập ñến ở ñây. ðiều cần nhấn mạnh là những hàm và thủ tục chúng ta ñã giới thiệu ở Interface thì bắt buộc phải ñược xây dựng ở ñây bởi lẽ chúng chính là những gì mà các chương trình tham chiếu ñến Unit cần sử dụng. Ngược lại trong phần này chúng ta có thể xây dụng các hàm và thủ tục khác hoặc khai báo các biến, hằng mới không có trong phần Interface, chúng ñược xem là phần riêng của Unit mà các chương trình tham chiếu ñến Unit không ñược phép sử dụng. Ví dụ: IMPLEMENTATION Function dtcn( a,b: real) : real; Var s : real; Begin S:=a*b; Dtcn:=s; End; ... Function cvcn( a,b: real) : real; Var s : real; Begin S:=a*b; Dtcn:=s; End; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 73 4.4 Phần khởi ñộng Phần khởi ñộng ñặt giữa hai từ khoá BEGIN và END, sau End là dấu chấm giống như các chương trình Pascal bình thường. Trong phần này chúng ta có thể ñưa vào các lệnh gán giá trị ban ñầu cho biến hoặc lời gọi các thủ tục riêng nào ñó cần cho quá trình tạo hàm và thủ tục của phần IMPLEMENTATION . Phần khởi ñộng là không bắt buộc phải có, trong trường hợp không có phần này thì chúng ta bỏ ñi từ khoá BEGIN song vẫn phải có từ khoá END. ñể báo hiệu kết thúc Unit. Dưới ñây là cấu trúc tổng thể của một Unit. Unit Tên_Unit ; Interface ..... Uses Tên_Unit; (tên các Unit sẽ dùng trong các chương trình con của Unit này) Const .... (khai báo hằng) Type ... (khai báo kiểu dữ liệu) Var ... (khai báo biến cho các chương trình con trong Unit ) Tên các Procedure và Function của Unit Implementation (nội dung của từng chương trình con) Begin Phần khởi tạo giá trị ban ñầu (tuỳ chọn) End. Về bản chất Unit cũng là một chương trình của Pascal, nó ñược xây dựng trên cơ sở các từ khoá và từ vựng mà Pascal ñã thiết kế do vậy từ bên trong Unit chúng ta lại có thể tham chiếu ñến các Unit khác không phân biệt là Unit chuẩn của Pascal hay Unit do người sử dụng tạo ra. Ví dụ 3.1 Xây dựng Unit HHP chứa hàm tính diện tích hình chữ nhật. Unit HHP; Interface Function dtcn( a,b: real) : real; Implementation Function dtcn( a,b: real) : real; Uses crt, dos; Var s : real; Begin S:=a*b; Dtcn:=s; End; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 74 Sau khi ñã soạn thảo song Unit chúng ta cần ghi Unit thành tệp với tên tệp trùng với tên ñã chọn ở phần tiêu ñề. Các Unit lúc này mới chỉ ở dạng tệp văn bản nên khi ghi vào ñĩa Pascal sẽ gán phần ñuôi là .Pas. Các chương trình Pascal có thể tham chiếu ñến các Unit này nhưng Pascal sẽ phải làm công việc biên dịch trước do ñó sẽ mất một thời gian. Tốt nhất ñể có thể sử dụng các Unit chúng ta phải dịch chúng sang dạng mã máy thông qua chức năng Compile trên thực ñơn chính của Pascal . Trước khi dịch cần chú ý rằng tuỳ chọn Destination của thực ñơn Compile phải là Disk chứ không phải là Memory. Có thể dùng một trong ba cách dịch ñã biết trong chức năng này là Make, Build, hoặc Run tuy nhiên cách hay ñược dùng là từ màn hình soạn thảo Unit, bấm tổ hợp phím Alt + F9, nếu không gặp một lỗi nào Pascal sẽ dịch Unit sang dạng mã máy và tự ñộng lưu tệp ñã biên dịch vào thư mục TP\BIN. Tệp này sẽ có tên giống như tên tệp nguồn nhưng phần mở rộng sẽ là TPU. Các TPU có thể lưu vào thư mục khác do người sử dụng tự chọn hoặc thư mục UNITS của Pascal. Nếu lưu và thư mục khác thì người sử dụng cần khai báo ñường dẫn ñến nơi lưu trữ thông qua lựa chọn Options – Directories – Unit Directories. Trong trường hợp việc dịch gặp lỗi Pascal sẽ thông báo lỗi như khi dịch một chương trình thông thường. Chú ý: Nếu tên tệp nguồn của Unit trong thư mục TP\BIN khác với tên ghi sau từ khoá Unit thì việc dịch không thông báo lỗi, song Pascal không tạo ra ñược TPU. Lệnh tham chiếu Uses TênUnit trong các chương trình trước hết sẽ tham chiếu ñến các TPU, nghĩa là ñến các Unit ñã biên dịch, nếu không tìm thấy các TPU, Pascal sẽ tìm các chương trình nguồn của Unit và tự ñộng biên dịch chúng trước khi thực hiện chương trình. Ví dụ 3.2: xây dựng Unit Chúng ta sẽ xây dựng một Unit lấy tên là HHP (hình học phẳng). Unit này sẽ tạo nên một số hàm dùng ñể tính diện tích , chu vi các hình tròn, chữ nhật và tam giác. ðối với tam giác chỉ xét trường hợp biết kích thước ba cạnh, bạn ñọc có thể thêm vào ñây các hàm mới ñể tính cho hai trường hợp còn lại của tam giác (cạnh – góc – cạnh và góc – cạnh – góc) hoặc tính thể tích, diện tích cho các hình không gian... Unit HHP; Interface Function Dtcn( a,b:real): real; Function Dttr( a:real): real; Function Dttg( a,b,c:real): real; Function Cvcn( a,b:real): real; Function Cvtr( a:real): real; Function Cvtg( a,b,c:real): real; Implementation Function Dtcn( a,b:real): real; Var dt:real; Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 75 Begin dt:=a*b; dtcn:=dt; End; Function Dttr( a:real): real; Var dt:real; Begin dt:=pi*a*a; dttr:=dt; End; Function Dttg( a,b,c:real): real; Var p,dt:real; Begin p:=(a+b+c)/2; dt:=sqrt(p*(p-a)*(p-b)*(p-c)); dttg:=dt; End; Function Cvcn( a,b:real): real; Var cv:real; Begin cv:=(a+b)*2; cvcn:=cv; End; Function cvtr( a:real): real; Var cv:real; Begin cv:=2*pi*a; cvtr:=cv; End; Function cvtg( a,b,c:real): real; Var cv:real; Begin cv:=a+b+c; cvtg:=cv; End; End. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 76 4.5 Phần hướng dẫn Mỗi Unit khi xây dựng xong cần có phần hướng dẫn ñể người sử dụng không gặp phải các lỗi khi chương trình tham chiếu ñến Unit, chẳng hạn trong Unit HHP khi tính diện tích chu vi tam giác sẽ có thể xảy ra trường hợp số ño các cạnh a, b, c không tạo thành tam giác. Trong trường hợp này người thiết kế Unit phải hướng dẫn người sử dụng kiểm tra ñiều kiện tạo nên tam giác trước khi gọi các Function của HHP. Thông thường phần hướng dẫn của Pascal ñặt trong thư mục DOC dưới dạng các tệp văn bản (phần mở rộng là TXT). Do bộ mã dùng trong Pascal không có các ký tự tiếng Việt nên phần hướng dẫn chỉ có thể viết bằng tiếng Việt không dấu. Mặc dù có phần hướng dẫn song nhiều khi người lập trình không ñọc kỹ hoặc ñọc mà vẫn quên các ñiều kiện của bài toán do vậy cần cố gắng hạn chế những ràng buộc mà người sử dụng phải tuân theo. ðể làm ñược ñiều này cần phải chú ý ñến các lớp bài toán có thể tham khảo ñến Unit, ñồng thời phải lường trước những sai sót mà người sử dụng có thể mắc phải. Trong thực tế ngay cả người lập trình chuyên nghiệp cũng không thể nào tránh ñược sai lầm do vậy cần chú ý ñến các thông báo lỗi khi người sử dụng ñưa ra các lệnh không phù hợp. Ví dụ 3.3 sau ñây nêu cách sử dụng Unit HHP tính diện tích chu vi tam giác Program tinh_dtcv; Uses crt,hhp; Var m,n,q:real; Begin clrscr; Write('Cho biet ba canh '); readln(m,n,q); if (m+n>q) and (n+q>m) and (q+m>n) then Begin Writeln('Dien tich tam giac la ',dttg(m,n,q):5:2); Writeln('Chu vi tam giac la ',cvtg(m,n,q):5:2) End Else Writeln('So lieu da cho khong tao thanh tam giac'); readln; End. Trong ví dụ trên nếu người chạy chương trình nhập vào giá trị của ba cạnh là 1, 3, 8 và trong chương trình không kiểm tra ñiều kiện tạo nên tam giác thông qua câu lệnh if (m+n>q) and (n+q>m) and (q+m>n) then ... thì sẽ dẫn tới diện tích là căn bậc hai của một số âm. Chúng ta có thể thiết kế lại Unit sao cho khi các tham số truyền cho chương trình con không phù hợp thì sẽ xuất hiện thông báo lỗi và yêu cầu nhập vào các giá trị khác. Muốn thế chúng ta phải thay thế các Function tính diện tích và chu vi tam giác thành các Procedure. Thông thường các Unit ñược thiết kế chưa thể bao quát ngay mọi yêu cầu mà các bài toán ñặt ra vì vậy việc chỉnh sửa, nâng cấp là ñiều khó tránh khỏi. ðể làm việc này người thiết kế Unit nên lưu trữ các cấu trúc dữ liệu, các gợi ý và cách thức tổ chức Unit ñể khi cần có thể Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 77 tra cứu dễ dàng. ðiều này có ý nghĩa khi chúng ta nên cung cấp cho người dùng không chỉ các TPU mà cả tệp nguồn với mục ñích người sử dụng có thể tự nâng cấp khi cần. 5. Tham chiếu ñến các Unit Một chương trình có thể tham chiếu ñến nhiều Unit cùng một lúc, các Unit này có thể là Unit chuẩn của Pascal hay là Unit tự tạo. Trong các Unit tự tạo người thiết kế cũng có thể ñưa vào các lệnh tham chiếu ñến các Unit khác. Pascal có một nguyên tắc ñặt ra là trong một chương trình các Unit không ñược tham chiếu vòng tròn. Giả sử chúng ta ñã thiết kế ba Unit là U1, U2, U3 với phần tiêu ñề và Interface như sau: Unit U1 ; Interface Uses U2; .... End. Unit U2; Interface Uses U3; .... End. Unit U3; Interface Uses U1; .... End. Nếu trong một chương trình chúng ta có lệnh Uses U1, U2, U3; Thì sẽ dẫn ñến một tình trạng là U1 tham chiếu ñến U2, U2 tham chiếu ñến U3 và U3 tham chiếu lại U1, trong trường hợp này Pascal sẽ thông báo lỗi: Circular Unit Reference (Unit tham chiếu vòng tròn). ðây có thể là một lỗi khó hiểu ñối với người lập trình nếu như người ñó không biết ñược bản thân các Unit mà mình sử dụng ñang tham chiếu ñến những Unit nào. ðiều này có thể giải thích là do Unit U1 cần phải tham chiếu ñến U3 thông qua U2, nhưng U3 lại phải tham chiếu ñến U1 trong khi bản thân U1 chưa hoàn thiện do ñó sẽ gây lỗi. Một vấn ñề khác cũng cần phải chú ý là thứ tự tham chiếu ñến các Unit, ví dụ lệnh: Uses CRT, GRAPH; quy ước rằng CRT ñược tham chiếu trước, GRAPH tham chiếu sau. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 78 Trong trường hợp các TPU và chương trình tham chiếu ñến chúng có một số biến , hàm trùng tên và có một số ñại lượng ñược khởi tạo giá trị ban ñầu thì Pascal sẽ ưu tiên sử dụng các giá trị ñược khởi tạo cuối cùng. Chúng ta sẽ giải thích ñiều này qua ví dụ sau; Ví dụ 3.4 Unit u1; Interface Var traloi:char; Procedure P11; Implementation Procedure P11; Begin Writeln('Day la Unit U1'); End; Begin traloi:='K'; End. Unit u2; Interface Var traloi:char; Procedure P11; Implementation Procedure P11; Begin Writeln('Day la Unit U2'); End; Begin traloi:='C'; End. Program dung_unit; Uses crt,u1,u2; Begin Clrscr; if upcase(traloi)='C' then Begin p11; Writeln('Bien Traloi mang gia tri : ',traloi); Writeln('Thu tu tham chieu la U1, U2 ') Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 79 End Else Begin p11; Writeln('Bien tra loi mang gia tri : ',traloi); Writeln('Thu tu tham chieu la U2, U1 ') End; Readln; End. Ví dụ 3.4 xây dựng hai Unit U1 và U2, cả hai ñều có biến Traloi, và thủ tục P11. Thủ tục P11 chỉ làm một công việc là viết lên màn hình dòng chữ “Day la unit ...”. Trong U1 biến Traloi ñược gán giá trị là ‘K’ còn trong U2 biến này mang giá trị ‘C’. Chương trình Dung_Unit có lệnh tham chiếu uses crt, u1, u2; Do Unit U2 ñược tham chiếu sau U1 nên giá trị của biến Traloi ñược khởi tạo cuối cùng sẽ thuộc về U2 nghĩa là Traloi = “C”. Lúc này biểu thức ñiều kiện if upcase(traloi)='C' trong chương trình Dung_unit mang giá trị True do ñó trên màn hình chúng ta nhận ñược kết quả Day la Unit U2 Bien Traloi mang gia tri : C Thu tu tham chieu la U1, U2 Nếu chúng ta ñảo ngược lại thứ tự tham chiếu Uses crt, u2, u1; thì sẽ nhận ñược kết quả Day la Unit U1 Bien Traloi mang gia tri : K Thu tu tham chieu la U2, U1 Ví dụ trên cũng cũng chỉ ra rằng nếu trong các TPU chúng ta thiết kế có các hàm hoặc thủ tục trùng tên với nhau và trùng tên với hàm, thủ tục có trong chương trình chính, ñồng thời các biến (hàm) ñược gán giá trị ban ñầu ngay trong phần Implemetation thì khi chương trình tham chiếu ñến TPU nó sẽ lấy giá trị của biến (hàm) ñược gán cuối cùng. Nếu trong bản thân chương trình chính cũng có lệnh gán giá trị cho biến (hàm) (trùng tên với các biến (hàm) có trong TPU) thì Pascal coi ñây là giá trị ñược gán cuối cùng và giá trị này sẽ ñược sử dụng trong các ứng dụng. Nói tóm lại việc thiết kế Unit ñúng chưa cho chúng ta kết quả ñúng nếu chúng ta không không nắm ñược nguyên lý sử dụng chúng. Cách tốt nhất là tránh dùng các hàm và biến trùng tên nhau. Trong trường hợp các Unit ñược thiết kế phục vụ một lớp bài toán nào ñó và bắt buộc phải sử dụng một số thủ tục hoặc hàm trùng tên thì cần chỉ rõ cách thức sử dụng chúng trong phần hướng dẫn sử dụng. Trường ðại học Nông nghiệp 1 - Giáo trình Lập trình nâng cao ..............................................................- 80