Giáo trình Ngôn ngữ lập trình Pascal - Đại học Hoa Lư

Tìm phần tử lớn nhất trên cột Col, kể từ hàng Col đến hàng n của ma trận A. function MaxRow(A: Mat; Col,n: byte): byte; var i, k: byte; begin k:=Col; for i:= Col + 1 to n do if abs(A[i, Col]) > abs(A[k, Col]) then k:=i; MaxRow:= k end;

doc141 trang | Chia sẻ: truongthinh92 | Lượt xem: 2297 | Lượt tải: 1download
Bạn đang xem trước 20 trang tài liệu Giáo trình Ngôn ngữ lập trình Pascal - Đại học Hoa Lư, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
n bản thành một dãy: Tap tin van ban Text CR LF 12345 CR LF Het Eof Các thủ tục Assign, Rewrite, Reset, Write, Read, Close, Erase, Rename đều dùng được cho tập tin văn bản. Ngoài ra còn có thêm thủ tục Append(biếntậptin) dùng để mở tập tin văn bản và cho phép ghi thêm dữ liệu vào cuối tập tin. Ðối với tập tin văn bản, không thể đồng thời vừa ghi vừa đọc dữ liệu như tập tin có định kiểu. Ðể ghi dữ liệu, trước tiên phải khởi tạo tập tin bằng lệnh Rewrite hay mở tập tin và đưa trỏ về cuối tệp bằng lệnh Append. Sau đó ghi dữ liệu vào tập tin bằng thủ tục Write hay Writeln. Ðể đọc dữ liệu một tập tin đã có, trước tiên ta phải mở tập tin bằng lệnh Reset. Sau đó đọc dữ liệu bằng thủ tục Read hay Readln. Nếu mở tập tin bằng Rewrite hoặc Append thì không thể đọc được bằng Read và Readln. Nếu mở tập tin bằng Reset thì không thể ghi được bằng Write hay Writeln. Ghi dữ liệu vào tập tin văn bản Thủ tục WRITE(biếntậptin, bt1, bt2, ..., btN): cho phép tuần tự ghi các giá trị của các biểu thức bt1, bt2, .. btN vào tập tin văn bản. Các biểu thức bt1, bt2, .. btN phải thuộc kiểu đơn giản chuẩn (nguyên, thực, ký tự, lôgic) hay kiểu chuỗi, và chúng không cần phải có kiểu giống nhau. Ví dụ: Write(F, 3, 10:4, ‘a’:2, ‘Text’, 3.5:6:2); sẽ ghi vào tập tin thành dãy như sau (Dấu ~ hiểu là một ký tự trắng): 3~~10~aText~~3.50 Chương trình dưới đây sẽ tạo tập tin văn bản T1.TXT: rogram VIDU; Var F: Text; A: Integer; B: Real; Begin A:=100; B:=1233.5; Assign(F, ’T1.TXT’); Rewrite(F); Write(F, ‘Ket qua=’:10, A:5, B:7:2); Close(F) End. Nội dung của tập tin T1.TXT là: ~~Ket qua=~~100~123.45 Như vậy, cách ghi dữ liệu vào tập tin văn bản hoàn toàn giống như khi in dữ liệu lên màn hình. Thủ tục WRITELN cũng có công dụng như WRITE, nhưng ghi xong dữ liệu thì đưa con trỏ tập tin xuống dòng dưới. Ðặc biệt, lệnh Writeln(F); không ghi gì cả, chỉ đưa con trỏ tập tin xuống dòng. Ðọc dữ liệu của tập tin văn bản Thủ tục READ(biếntậptin, biến1, biến2, ..., biếnN) đọc tuần tự các giá trị từ tập tin và gán cho các biến. Các biến1, biến2, ..., biếnN phải có kiểu dữ liệu phù hợp vơí dữ liệu cần đọc tại vị trí tương ứng trong tập tin. Ví dụ: Nếu tập tin T1.TXT có nội dung như sau: ~~Ket qua=~~100~123.45 thì để đọc lại các dữ liệu này, ta phải khai báo: Var St:String[10]; i: Integer ; Z: Real; Và dùng các lệnh: Reset(F); Read(F, St, i, Z); Giá trị của St, i, Z sẽ là: St=’~~Ket qua=’, i=100, Z=123.44. Nếu khai báo St có kiểu String[9] thì sẽ bị lỗi vì sau khi đọc xong 9 ký tự đầu, máy sẽ đọc tiếp giá trị =~~100 cho biến nguyên i, nhưng vì giá trị này bắt đầu là dấu = nên không đổi ra số nguyên được. Thủ tục READLN(biếntậptin, biến1, biến2, ..., biếnN ) đọc dữ liệu cho các biếN xong sẽ đưa trỏ tập tin xuống đầu dòng dưới. Ðặc biệt, lệnh READLN(biếntậptin); không đọc gì cả, chỉ đưa con trỏ tập tin xuống dòng. Chú ý Hàm Eof(F) cũng dùng được cho tập tin văn bản, ngoài ra còn có hàm EOLN(F) cho kết quả là True hoặc False tùy theo con trỏ tập tin có đang ở cuối dòng hay không. Khi Eof(F)=True thì Eoln(F) cũng có giá trị là True. Thủ tục Seek và các hàm FileSize, FilePos không dùng cho tập tin văn bản. Các tập tin văn bản ngầm định: Trong Pascal có hai biến tập tin văn bản đã được khai báo sẵn là Input và Output , tức là máy đã ngầm khai báo: Var Input , Output: Text ; Input thường là bàn phím còn Output thường là màn hình. Lệnh Readln(Input, x); được viết tắt thành Readln(x) ; Lệnh Writeln(Output, x); được viết tắt thành Writeln(x); Máy in cũng là một tập tin văn bản, được ngầm khai báo với tên là LST. Ðể in các biểu thức bt1, bt2, ..., btN ra máy in, ta phải khai báo sử dụng thư viện chuẩn PRINTER, và dùng lệnh: Write(LST, bt1, bt2, ... , btN); So sánh tập tin văn bản với tập tin định kiểu: Các tập tin có định kiểu cho phép vừa đọc vừa ghi và truy nhập trực tiếp vào từng phần tử gần giống như thao tác với mảng. Các tập tin văn bản không cho phép đồng thời đọc, ghi và chỉ có thể đọc hoặc ghi tuần tự, nhưng cho phép ta có thể xem, sửa trực tiếp một cách dễ dàng bằng các hệ soạn thảo văn bản đơn giản, như NC hay chính Turbo Pascal. CÂU HỎI ÔN TẬP  Câu 1: Cho khai báo biến: Var m, n: integer; x, y: Real; Lệnh nào sai:  a) m:= -4; b) n:= 3.5; c) x:= 6; d) y:= +10.5; Câu 2: Ðể tính giá trị , chọn cách viết nào:  a) x:= -b/2a; b) x:= -b/2*a; c) ; d) x:= -b/2/a; Câu 3: Biểu thức: 25 div 3 + 5/2*3 có giá trị là:  a) 8.0; b) 14.5; c) 9.5; d) 14.0; Câu 4: Cho ch là biến có kiểu Char. Lệnh nào đúng:  a) ch:="a" b) ch:=65; c) ch:=chr(65); d) ch:='abcd'; Câu 5:Biến X được khai báo là kiểu integer. Lệnh nào sai:  a) X:= round(275/3); b) X:= 210 div 4; c) X:= SQRT(49); d) X:= ABS(-453); Câu 6: Biểu thức nào sau đây có giá trị TRUE:  a) (100 > 76) and ('B' 4 div 2);  c) (49.5 + 2 < 5) and (2 < 4 div 2); d) 2*(3+5) < 18 div 4*4; Câu 7: Khi chạy chương trình: Var St, St1: String; Begin St:= '123'; St1:= '456'; Write(St + St1) End; Kết quả in ra là: a) '123456'; b) 123456; c) 579; d) Câu a), b), c) đều sai; Câu 8: Sau phép gán: Ch:= CHR(ORD('a')- 32 ); thì giá trị của Ch là:  a) 65; b) A; c) 'A'; d) 'a'; Câu 9: Khi chạy chương trình: Var a, b, c, N: integer; Begin N:=546; a:=N div 100; b:=(N Mod 100) div 10; c:=(N Mod 100) Mod 10; Write(a+b+c) End. Kết quả in ra: a) 546; b) 5; c) 15; d) 6; Câu 10: Trong khai báo sau còn bỏ trống . . . một chỗ, vì chưa xác định được kiểu dữ liệu của biến Max: Var A: Array[‘a’..’d’] of Real ; Ch: Char ; Max: . . . ; Muốn biến Max lưu giá trị lớn nhất của mảng A thì cần khai báo biến Max kiểu gì vào chỗ . . .: a) Char b) Integer c) String d) Real Câu 11: Khai báo nào đúng: a) Var A: array[1..n,1..m] of integer; b) Const n=2; m=3; Var A: array[1..n,1..m] of integer; c) Var n, m: integer ; A: array[1..n,1..m] of integer; d) Var A: array[3, 2] of Integer; Câu 12: Cho biến SS kiểu logic. Lệnh nào làm SS có giá trị là TRUE: a) SS:= 'a' < 'A'; b) SS:= 'A' = 'a'; c) SS:= 'an' < 'a'; d) SS:= 'PASCAL' < 'pascal'; Câu 13: Cho khai báo: Var Ho, ten: String[15]; Lệnh nào sai: a) Write('Ho ten la: ' ; Ho ; Ten); b) Write('Ho ten la: ' + Ho + Ten); c) Write('Ho ten la: ', Ho , Ten); d) Write('Ho ten la: ', Ho + Ten); Câu 14: Cho khai báo: Var Chuoi: string[10]; x: real; Lệnh nào đúng: a) Chuoi:= Str(x:5:2) ; b) Str(x:5:2, Chuoi); c) Chuoi:= x ; d) x:= Chuoi ; Câu 15: Cho St là biến chuỗi, sau khi thực hiện hai lệnh: St:= Copy('PASCAL VERSION 4.5' , 8, 7) ; Write(St); Kết qủa in lên màn hình là: a) VERSION 4.5 b) VERSION c) PASCAL d) 4.5 Câu 16: Cho St là biến chuỗi, sau khi thực hiện bốn lệnh: St:=’ABCDEF’; Delete(St, 3, 2); Insert(‘XYZ’, St, 2); Write(St); Kết qủa in lên màn hình là: a) ABXYZEF b) AXYZBCDEF c) AXYZ d) AXYZBEF Câu 17: Cho i và x là hai biến kiểu nguyên. Khi thực hiện lệnh: VAL('1234', x, i); Giá trị của x và i là bao nhiêu: a) x = 0 , i = 1234 b) x = 1234 , i = 4 c) x = 1234 , i = 0 d) x = 0 , i = 0 Câu 18: Cho các biến St chuỗi và k nguyên. Sau khi gán: St:='Sinh vien Tin hoc hoc Tin hoc'; k:= Pos('Tin', ST) ; Giá trị của k là: a) k=13 b) k=11 c) k=26 d) k=23 Câu 19: Chọn khai báo đúng: a) Type Phanso = Record Tu, Mau: Integer end ; b) Type Phan so = Record Tu so, Mau so: Integer end; c) Var Phanso = Record ; Tu, Mau: Integer end ; d) Type Record = Phanso ; Tu, Mau: Integer end; Câu 20: Cho khai báo: Type SV = Record Ten:String[20]; Dtb:Real end; Var X, Y: SV ; Chọn câu đúng: a) SV.Dtb:= 3.5 ; b) X.Dtb:=’ Le Van Toan’; c) X.Ten:=’Anh’ ; d) X:= Y.Dtb; Câu 21: Cho khai báo: Type SV = Record Ten:String[20]; Dtb:Real end; Var X: SV ; Chọn câu đúng: a) With X do Dtb:= 3.5 ; b) Dtb:= 3.5 ; c) With SV do Dtb:=3.5 ; d) X:= 3.5 ; Câu 22: Cho khai báo: Type SVIEN = Record Ten, Maso: String[10] end; Câu nào đúng: a) Var A: array [1..10] of Record; b) Var A: array [1..10] of SVIEN; c) Var A: array[1..10] of Ten ; d) A: array[1..10] of Maso ; Câu 23: Khi chạy chương trình: Type Vector = Record x, y: Integer end; Var A, B: Vector ; Begin A.x:= 2 ; A.y:= 1; B.x:= -1; B.y:= -3 ; Write(A.x*B.x+A.y*B.y) End. Kết qủa in ra là: a) 2 b) -5 c) -7 d) -3 Câu 24: Cho khai báo: Type Toado = Record hoanhdo, tungdo: Integer end; Var A, B: Toado ; Lệnh nào sai: a) A:=B ; b) A:=A-1; c) A.hoanh:=B.tung ; d) With A do hoanh:= B.hoanh; Câu 25: Khai báo nào đúng, khai báo nào sai: a) Type T = Set of Real; b) Type T = Set of Integer; c) Type T = Set of String ; d) Type T = Set of 0..9 ; e) Var T: Set of Char ; f) Var T: Set of -10..300 ; g) Var T: Set of ‘A’..’z’ ; h) Var T: Set of Boolean ; Câu 26: Tổng của [1..5] + [3..10, 20] là tập hợp nào: a) [1..10, 20] b) [1..5] c) [1..20] d) [3..5] Câu 27: Hiệu của hai tập hợp [10..15]-[3..12] là tập hợp nào: a) [3..15] b) [13..15] c) [3..10] d) [10..12] Câu 28: Cho khai báo: Var F: File of Integer ; i , j , k:Integer ; Chọn câu có các lệnh đều đúng: a) Assign(F, T1.DAT ); Rewrite(F); Write(F, i, j, k ); b) Assign(F, ‘T1.DAT’ ); Rewrite(F); Write(F, i+ j+ k); c) Assign(F); Rewrite(F, ‘T1.DAT’ ); Write(F, i, j, k ); d) Assign(F, ‘T1.DAT’ ); Rewrite(F); Write(F, i, j, k ); Câu 29: Khi chạy chương trình: Var F: File of Char; ch: Char; Begin Assign(F, ‘tt.txt’); Rewrite(F); For ch:=’A’ to ‘F’ do Write(F, ch); Seek(F, 4); Read(F, ch); Write(ch); Close(F) End. Kết quả in ra là: a) E b) F c) C d) D Câu 30: Cho khai báo: Var F: TEXT ; Sau khi thực hiện các lệnh: Assign(F, ‘tt1.txt’); Rewrite(F); Write(F, 123+456); Close(F); Nội dung của tập tin tt1.txt sẽ là: a) 123+456 b) 123456 c) 579 d) 123 456 Câu 31: Cho F1 là biến tập tin có định kiểu và F2 là biến tập tin văn bản. Lệnh nào không dùng được: a) Seek(F1, 0); b) Seek(F2, 0); c) Write(Filesize(F1) ) ; d) Write(FilePos(F1)) ; Câu 32: Cho TT2.TXT là tập tin văn bản có nội dung là: Turbo Pascal 7.0 Khi chạy chương trình: Var F: Text; St: String[20]; Begin Assign(F, ‘tt2.txt’); Reset(F); Read(F, St); Write(St); Close(F) End. sẽ in ra chữ: a) Turbo Pascal 7.0 b) Turbo c) Pascal 7.0 d) Turbo Pascal BÀI TẬP LẬP TRÌNH Câu 1. Nhập x thực, n nguyên dương, tính gần đúng cosx: Câu 2. Nhập số nguyên dương N, cho biết số đó có bao nhiêu chữ số, và chữ số lớn nhất là bao nhiêu. Ví dụ: số N = 1275 có bốn chữ số, chữ số lớn nhất là 7. Câu 3. Tìm và in lên màn hình tất cả các số nguyên dương có ba chữ số (trong phạm vi từ 100 đến 999) sao cho tổng các bình phương của các chữ số của nó bằng 24. Ví dụ:số N=304 có ba chữ số là 3, 0 và 4, và 32+02+42 = 24. Tương tự đối với số 500. Câu 3. Nhập số N nguyên dương, tính: Câu 4. Nhập ngày, tháng, năm sinh của bạn. Từ đầu năm sinh đến ngày tháng năm sinh của bạn có bao nhiêu ngày?. Ví dụ, sinh ngày 17/2/1977 thì từ đầu năm 1977 đến ngày đó có 48 ngày. Câu 5. Nhập số N nguyên dương, tính S là tổng của N số nguyên tố đầu tiên. Ví dụ N=3 thì S=2+3+5=10. Câu 6. Tìm số dương nhỏ nhất trong dãy x1, x2,..., xn . Câu 7. Sắp xếp dãy x1, x2,..., xn sao cho các số dương đứng trước theo thứ tự giảm dần, rồi đến các số còn lại (số âm và số 0) theo thứ tự tăng dần. Ví dụ, nhập dãy 3, 0, 4, -5, 2, -1, 7, 0, -6, sắp thành: 7, 4, 3, 2, -6, -5, -1, 0, 0. Câu 8. Nhập một dãy số nguyên dương x1, x2,..., xn . Tìm bội số chung nhỏ nhất của chúng. Ví dụ dãy 1 2 5 4 6 3 5 có bội số chung nhỏ nhất là 60. Câu 9. Nhập và in ma trận Am,n .Cho biết hàng 1 và hàng 2 có giống nhau không, nếu không thì hãy hoán đổi hàng 1 và hàng 2. Ví dụ: ma trận bên trái dưới đây có hàng 1 và hàng 2 không trùng nhau, sau khi hoán đổi hai hàng ta được ma trận bên phải: Câu 10. Nhập và in ma trận Am,n .Hãy hoán đổi các hàng của ma trận A sao cho các phần tử của cột một lập thành một dãy tăng. Câu 11. Nhập và in ma trận Am,n . Tìm số dương nhỏ nhất trong ma trận. Câu 12. Nhập và in ma trận Am,n các số nguyên dương. Tìm bội số chung nhỏ nhất của tất cả các phần tử của ma trận. Câu 13. Nhập chuỗi St, xây dựng chuỗi St1 gồm các ký tự của St nhưng đảo ngược thứ tự. Ðổi chuỗi St thành chữ hoa và đổi chuỗi St1 thành chữ thường. Ví dụ cho St=‘AbcD12’, thì St1=‘21DcbA’, sau khi đổi ta được St= ‘ABCD12’ và St1=‘21dcba’. Câu 14. Nhập chuỗi St, đếm xem trong chuỗi có bao nhiêu chữ a không phân biệt viết hoa hay viết thường, và cho biết vị trí của các chữ a đó. Ví dụ St=‘Anh van la quan trong’ có 4 chữ a tại các vị trí 1, 6, 10, 13. Câu 15. Nhập chuỗi St, cho biết trong St có bao nhiêu ký số ‘0’, ‘1’, ‘2’, ..., ‘9’ mỗi loại. Ví dụ St=‘13163’, in ra: có 2 ký số 1, có 2 ký số 3, có 1 ký số 6, các loại khác không có. Câu 16. Nhập chuỗi St, xóa bỏ các ký tự trắng thừa ở đầu và cuối chuỗi, và sao cho giữa hai từ chỉ có đúng một ký tự trắng, đổi chuỗi thành chữ thường, riêng các chữ đầu từ thành chữ hoa. Ví dụ: St =‘ Hom nAy tHUC taP ‘. In ra: ‘Hom Nay Thuc Tap’. Câu 17. Nhập một chuỗi St, đếm xem trong St: có bao nhiêu chữ cái A,B,C,..,Z có bao nhiêu chữ số 0,1, 2, 3,.., 9 có bao nhiêu ký tự trắng có bao nhiêu các ký tự khác. Trong bốn loại trên thì loại nào nhiều nhất ? Câu 18. Nhập hai chuỗi St và St1. Cho biết chuỗi St1 xuất hiện mấy lần trong St, và tại các vị trí nào?. Ví dụ St=‘pas12pas34’, chuỗi St1 =‘pas’ xuất hiện 2 lần tại các vị trí 1, 5. Câu 19. Nhập ba chuỗi St, St1, St2. tìm xem trong chuỗi St có chứa chuỗi St1 không ?, nếu có thì thay thế St1 bằng St2. Ví du: cho St=‘ABC1234E’, St1=‘1234’ và St2 =‘*’. Sau khi thay thế ta được St=‘ABC*E’. Câu 20. Nhập một chuỗi St gồm nhiều từ. Giả thiết St có không quá 20 từ, mỗi từ dài không quá 10 ký tự. Xây dựng một mảng A chứa các từ của St, với A[i] chứa từ thứ i của St. Sắp xếp và in các từ của mảng A theo trật tự giảm của độ dài của từ. Ví dụ cho St:=‘Thanh pho THAI NGUYEN’ thì: A[1]=‘Thanh’, A[2]= ‘pho’, A[3]=‘THAI’, A[4]=‘NGUYEN’. In ra: NGUYEN Thanh THAI pho. Câu 21. Ðể biết một thí sinh đỗ hay trượt trong kỳ thi tuyển sinh, cần biết các thông tin sau: Họ tên: họ và tên của thí sinh KV: thí sinh thuộc khu vực nào? (1, 2 hay 3 ). NH: thí sinh thuộc nhóm nào ? (1, 2, hay 3). TÐ: tổng điểm ba môn thi . Hãy nhập vào một danh sách 100 thí sinh gồm Họ tên, KV, NH, TÐ. Xét xem Kết qủa thí sinh này đỗ hay trượt dựa vào bảng điểm chuẩn sau: Ví dụ: Thí sinh ở Khu vực 1 , Nhóm 2, có Tổng điểm ³ 17.5 thì đậu, ngược lại thì rớt. b) In danh sách đã sắp xếp theo trật tự giảm của TÐ lên màn hình, gồm các mục Họ tên, KV, NH, TÐ và Kết qủa đậu, rớt. c) Chỉ in danh sách những người đậu lên màn hình. Câu 22. Mỗi điểm M(x,y) trong mặt phẳng toạ độ Oxy được mô tả như sau: Type DIEM = Record x, y: real End; Viết chương trình với các yêu cầu sau: Có một thủ tục nhập vào tọa độ của ba điểm A, B, C . Trong đó có một hàm kiểm tra xem có một cặp điểm nào trùng nhau không ?. Nếu có thì bắt nhập lại cho đến khi được ba điểm A, B, C phân biệt. Có một thủ tục (hay hàm) tính độ dài các cạnh AB, BC, AC theo công thức: Nếu A(xa,ya), B(xb,yb) thì Có một thủ tục kiểm tra xem AB, BC, AC có phải là ba cạnh của một tam giác không? (Hd: tổng hai cạnh phải lớn hơn cạnh còn lại ). Nếu không phải thì in ra chữ " Không phải Tamgiác" , ngược lại thì tính và in diện tích của tam giác ABC lên màn hình theo công thức Hêrông:   , với P là nửa chu vi tam giác: P = (AB+BC+AC) / 2 . Câu 23. Nhập một chuỗi St, xây dựng ba tập hợp: S1 là tập các chữ hoa có trong St S2 là tập các chữ thường có trong St S3 là tập các chữ số có trong St In các giá trị của mỗi tập S1, S2, S3 trên một dòng. Câu 24. Nhập vào một mảng A1, A2, ..., A10 các số nguyên dương < 10. Hãy in các giá trị của mảng này theo thứ tự tăng dần sao cho các phần tử trùng nhau chỉ được in một lần. Ví dụ: cho mảng 1, 6, 4,1, 9, 6, 6, 0, 3, 9 , in ra: 0,1, 3, 4, 6, 9 (Hướng dẫn: xây dựng tập hợp gồm các phần tử của mảng A ) Câu 25. Nhập số nguyên dương N (0<N< 20 ) và một dãy N số nguyên: A1, A2, ..., AN . Ghi dãy số đó vào tập tin DL.DAT. Ðếm trong tập tin DL.DAT có bao nhiêu số chẵn. Ðọc các số lẻ từ tập DL.DAT và in lên màn hình. Câu 26. Ðể quản lý Họ tên , các điểm Toán, Lý và Ðiểm trung bình của sinh viên, ta mô tả kiểu Ksvien như sau: Type Ksvien= Record Hoten: String[20];Toan, Ly, Dtb: Real end; Hãy nhập một danh sách sinh viên gồm Họ tên, điểm Toán và điểm Lý, rồi tính Ðiểm trung bình: Dtb:=(Toán+Lý)/2, lưu vào tập tin QLY.DAT. Quá trình nhập kết thúc khi nhập Họ tên là rỗng (tức không nhập gì cả, cứ Enter). Ðọc danh sách sinh viên từ tập tin QLY.DAT và in danh sách lên màn hình. Chép danh sách sinh viên vào tập QLY.IDX sao cho các phần tử của QLY.IDX được sắp xếp theo trật tự giảm của điểm trung bình. Ðọc dữ liệu của tập tin QLY.IDX và ghi vào tập tin văn bản tên là QLY.TXT theo dạng: STT Họ và tên Ðiểm trung bình 1 Nguyen Van Tuan 8.5 2 Pham Thi Mai 8.0 Câu 27. Dùng hệ soạn thảo của Turbo Pascal để tạo một tập tin văn bản có tên là MT.DAT chứa hai ma trận vuông cấp 3 là A và B có các phần tử là các số nguyên. Lấy dữ liệu từ tập tin MT.DAT để tính ma trận C=A+B. Ghi ma trận C vào cuối tập tin MT.DAT. Ðọc các ma trận A, B, C từ tập tin MT.DAT và in lên màn hình. Bài 7: LẬP TRÌNH NÂNG CAO 1. Con trỏ và biến động 1.1. Khái niệm Khi khai báo một biến, dù là biến đơn hay biến thuộc kiểu dữ liệu có cấu trúc mặc nhiên chúng ta đã quy định độ lớn vùng nhớ dành cho biến. Ví dụ a: integer; biến a cần 2 byte b: aray[1..100] of Integer; biến mảng b cần 200 byte. Việc khai báo như trên thường là phỏng đoán dung lượng cần thiết chứ không thật chính xác, để tránh lỗi chúng ta thường khai báo thừa ra gây nên lãng phí bộ nhớ. Việc xác định địa chỉ lưu trữ biến và cấp phát bộ nhớ được thực hiện khi biên dịch, nghĩa là các địa chỉ này cũng như dung lượng bộ nhớ cần cấp phát đã được cố định trước khi thực hiện các thao tác khác. Các đại lượng này không thay đổi trong suốt quá trình thực hiện chương trình, nói cách khác đây là các đại lượng tĩnh. Để tiết kiệm bộ nhớ, ngay khi chương trình đang làm việc người lập trình có thể yêu cầu cấp phát bộ nhớ cho các biến, điều này được gọi là cấp phát bộ nhớ động. Cấp phát bộ nhớ động được thực hiện thông qua biến con trỏ. Muốn có biến con trỏ chúng ta phải định nghĩa kiểu con trỏ trước. 1.2. Kiểu dữ liệu con trỏ - biến con trỏ a, Con trỏ có định kiểu Kiểu con trỏ là một kiểu dữ liệu đặc biệt dùng để biểu diễn các địa chỉ. Kiểu con trỏ do người lập trình định nghĩa theo cú pháp sau: Type Tên kiểu con trỏ = ^Kiểu dữ liệu; Tên kiểu con trỏ tuân theo quy định đặt tên của Pascal, Kiểu dữ liệu của kiểu con trỏ là các kiểu dữ liệu đã định nghĩa trước trong pascal. Để một kiểu con trỏ có thể đại diện cho một biến nào đó thì Kiểu dữ liệu viết sau ký tự ^ sẽ phải giống như kiểu dữ liệu của biến đó, nói cách khác hai kiểu dữ liệu phải tương thích. Ví dụ: khai báo kiểu con trỏ: Type Chu = string[20]; CT1 = ^Byte; CT2 = ^chu; CT3 = ^Nguoi; Nguoi = record Hoten:string[20]; Namsinh: 1900..2100; End; Chú ý: Pascal chỉ cho phép đưa trực tiếp vào định nghĩa kiểu con trỏ các kiểu dữ liệu đơn giản sau: số nguyên, số thực, ký tự. Các kiểu dữ liệu có cấu trúc muốn đưa vào con trỏ thì phải thông qua một tên kiểu khai báo trong phần Type b, Con trỏ không định kiểu Con trỏ không định kiểu là kiểu con trỏ không quan tâm đến kiểu dữ liệu mà nó trỏ tới. Pascal dùng tên chuẩn Pointer để khai báo kiểu này. Cú pháp: Var Tên biến:Pointer; Con trỏ không định kiểu được coi là tương thích với mọi kiểu con trỏ. Chú ý: * Về bản chất tất cả con trỏ đều chứa địa chỉ nên chúng không có gì khác nhau song để tránh nhầm lẫn trong các quá trình xử lý Pascal chỉ coi các con trỏ cùng trỏ tới một kiểu dữ liệu là tương thích với nhau. c, Biến con trỏ Biến con trỏ cũng như biến mảng, biến kiểu bản ghi hay kiểu tập hợp có thể khai báo thông qua kiểu con trỏ hoặc khai báo trực tiếp. Biến con trỏ có định kiểu sẽ trỏ đến một kiểu dữ liệu cụ thể. 1.3. Địa chỉ của một đối tượng Đối tượng mà chúng ta đề cập có thể là biến, hàm hay thủ tục. Khi biên dịch chương trình mỗi đối tượng được cấp phát một vùng nhớ, vùng nhớ này bao gồm một số ô nhớ liền kề nhau. Địa chỉ một đối tượng trong bộ nhớ được xác định bởi địa chỉ của ô nhớ đầu tiên mà hệ thống dành cho đối tượng đó. 1.4. Các thủ tục và hàm tác động trên con trỏ a, Gán giá trị ban đầu Giả sử p là một biến con trỏ đã được định nghĩa, để đảm bảo rằng p chưa trỏ đến bất kỳ một đối tượng nào, nghĩa là p là một con trỏ rỗng chúng ta phải gán cho p giá trị NIL p:= Nil; b, Gán địa chỉ của một đối tượng cho con trỏ Giả sử p là một con trỏ và x là một đối tượng (biến, hàm, thủ tục), có ba cách gán địa chỉ của đối tượng x cho con trỏ p: + p:= @x; Trong phép gán trên toán tử @ tác động trên đối tượng x sẽ gán vào con trỏ p địa chỉ kiểu Pointer của đối tượng đó. + p:= Addr(x); Hàm Addr() cho địa chỉ của đối tượng x, địa chỉ này thuộc kiểu Pointer + p:= Ptr(segment,offset) ; Hàm Ptr trong phép gán trên đòi hỏi các tham số segment và offset phải là giá trị kiểu Word viết trong hệ 16, ví dụ: p:= Ptr($B800, $0000); đưa con trỏ trỏ tới ô nhớ của vùng Video Ram c, Phép gán giữa hai con trỏ Hai con trỏ tương thích (cùng kiểu) có thể gán giá trị cho nhau, khi đó chúng cùng trỏ tới một địa chỉ. Ví dụ: Var p: ^Float; p1: ^Byte; p2: Pointer; x: string; Khi đó các phép gán: P2:=@x; P1:= p2; là hợp lệ vì p1 và p2 là tương thích, chúng cùng trỏ đến địa chỉ của biến x. Còn phép gán p:= p1; là không hợp lệ vì hai con trỏ không tương thích. d, Phép so sánh hai con trỏ Chỉ tồn tại phép so sánh = (bằng nhau) và (khác nhau) giữa hai con trỏ nếu chúng tương thích. Kết quả so sánh là một giá trị Boolean nghĩa là True hoặc False. Hai con trỏ tương thích gọi là bằng nhau nếu chúng cùng trỏ tới một đối tượng, ngược lại gọi là khác nhau. Chú ý: * Địa chỉ của một đối tượng có thể gán cho bất kỳ con trỏ nào. * Với con trỏ không định kiểu (Pointer) chúng ta không thể coi chúng là tương đương với các biến định kiểu thông thường, điều này có nghĩa là không thể sử dụng các thủ tục Write, Read hoặc phép gán cho biến p^ nếu p là Pointer. 1.5. Cấp phát động a, Quản lý vùng nhớ Heap Cấp phát bộ nhớ động là việc cấp phát bộ nhớ được thực hiện bởi câu lệnh trong thân chương trình chứ không phải bằng cách khai báo biến hoặc tham số. Khi một biến được cấp phát bộ nhớ động thì nó trở thành biến động (Dynamic Variable). Vùng nhớ dành cho cấp phát động bao giờ cũng là vùng nhớ tự do Heap. Theo mặc định khi một biến động được hình thành thì địa chỉ của nó được lưu trong biến con trỏ tương ứng. b, Thủ tục cấp phát bộ nhớ cho con trỏ định kiểu Với một con trỏ định kiểu p thủ tục cấp phát bộ nhớ động sẽ là: New(p); Thủ tục New(p) cùng một lúc thực hiện các công việc sau: * Cấp phát một vùng nhớ trên Heap với kích thước bằng kích thước kiểu dữ liệu mà con trỏ trỏ tới. * Tạo một biến động định kiểu p^ để có thể truy nhập vào vùng dữ liệu của đối tượng. * Lưu địa chỉ của đối tượng vào con trỏ p Khi một biến động p^ đã hết giá trị sử dụng thì cần thu hồi vùng nhớ đã cấp phát cho nó để dùng vào việc khác. Thủ tục thu hồi là Dispose(p); Sau khi thu hồi vùng nhớ mặc dù biến p^ vẫn còn tồn tại nhưng dữ liệu trong vùng nhớ đã dành cho p sẽ không được bảo vệ, điều này có nghĩa là hệ thống có thể dùng vùng nhớ này vào việc khác. c, Thủ tục cấp phát bộ nhớ cho con trỏ không định kiểu Pascal có hai thủ tục cấp phát và thu hồi vùng nhớ cho con trỏ không định kiểu p là: Getmem(p, pn); cấp cho p n Byte. Freemem(p, n); thu hồi n Byte vùng nhớ đã cấp cho p. Việc cấp phát không định kiểu thường được dùng trong trường hợp chưa biết trước kích thước của đôí tượng. 2.Danh sách liên kết 2.1. Định nghĩa Với con trỏ kiểu mảng thì việc lập trình tạo danh sách cũng như duyệt danh sách khá đơn giản, hạn chế của kiểu mảng là phải khai báo trước kích thước mảng do đó nhiều khi dẫn tới không tiết kiệm bộ nhớ. Sử dụng biến động chúng ta có thể khắc phục được nhược điểm này bằng cách cần đến đâu thì tạo biến động đến đó. Số biến động tạo ra chỉ bị hạn chế bởi bộ nhớ trong (Ram) của máy PC mà cụ thể là phụ thuộc vào kích thước vùng Heap. Danh sách được hiểu là một tập hợp hữu hạn các phần tử liên kết với nhau, trường hợp tổng quát nhất mỗi phần tử là một bản ghi. điều đặc biệt của mỗi bản ghi trong danh sách là ngoài các trường dữ liệu, còn một trường dùng để liên kết và trường này lại là một con trỏ. Con trỏ này có nhiệm vụ trỏ vào địa chỉ của bản ghi kế tiếp. Nếu bản ghi hiện thời là bản ghi cuối cùng thì con trỏ sẽ trỏ vào Nil. Chú ý: - Một danh sách chưa có phần tử nào đươc gọi là danh sách rỗng. - Việc thêm một phần tử vào danh sách có thể rơi vào một trong ba khả năng: * Phần tử mới được thêm vào đầu danh sách * Phần tử mới được nối vào cuối danh sách * Phần tử mới được chèn vào một vị trí xác định. Trường hợp 1 chúng ta có danh sách liên kết ngược (LIFO), còn trường hợp 2 chúng ta có danh sách liên kết thuận (FIFO) hay còn gọi là hàng đợi QUEUE. 2.2. Danh sách liên kết ngược Là loại danh sách mà trường liên kết của phần tử tạo ra sau luôn trỏ vào phần tử tạo ra trước đó. Trường liên kết của phần tử tạo ra đầu tiên trỏ vào Nil. điều này dẫn tới việc khi kết xuất thông tin ra chúng ta phải bắt đầu từ phần tử tạo ra cuối cùng vì chỉ có như vậy chúng ta mới biết địa chỉ của phần tử tạo ra trước đó. Nếu cố tình đi từ phần tử tạo ra dầu tiên thì không thể biết phần tử tiếp theo là phần tử nào. Liên kết kiểu này gọi là kiểu LIFO (Last In- Firt Out) hay còn gọi là kiểu xếp chồng. Phần tử nhập vào cuối cùng sẽ được lấy ra đầu tiên. Danh sách tạo ra theo kiểu này được gọi là danh sách liên kết ngược. Ví dụ: Giả thiết rằng chúng ta cần xây dựng một danh sách sinh viên với các trường dữ liệu là Mhs (mã hồ sơ), Hoten (Họ và tên), Diem (điểm tổng kết) , trường liên kết lấy tên là next (tiếp tục). Kiểu dữ liệu bản ghi với các trường nêu trên lấy tên là sinhvien. để tạo ra danh sách sinh viên chúng ta cần tạo ra một kiểu con trỏ DS trỏ vào kiểu dữ liệu sinhvien và trường liên kết next trong bản ghi sinhvien sẽ trỏ vào kiểu dữ liệu Ds. Type Ds = ^sinhvien; sinhvien= Record Mhs:byte; Hoten: string[20]; Diem:real; Next: Ds; End; Sau khi khai báo kiểu dữ liệu cần khai báo biến con trỏ Ds để lưu trữ dữ liệu nhập vào và biến Ctcuoi (con trỏ cuối) để trỏ vào phần tử cuối cùng. Vấn đề là làm thế nào để con trỏ liên kết next luôn trỏ vào phần tử tạo ra trước đó. Để làm việc này chúng ta tạo ra biến động Ds để lưu trữ dữ liệu. Cứ mỗi bản ghi cần nhập vào thì tạo ra một biến động Ds mới. địa chỉ của biến động Ds luôn được gán cho con trỏ cuối Ctcuoi. 2.3. Hàng đợi Queue - Danh sách liên kết thuận Với loại danh sách mà phần tử nào nhập trước thì được lấy ra trước chúng ta gọi là danh sách liên kết thuận, nó cũng giống như hàng đợi người nào đến trước thì được gọi trước. Để tạo ra hàng đợi ngoài ctcuoi chúng ta phải thêm vào con trỏ đầu (ctdau). Con trỏ cuối ctcuoi luôn trỏ vào phần tử cuối cùng, còn con trỏ đầu lại luôn trỏ vào phần tử đầu của danh sách. Program danh_sach_lien_ket_thuan; Uses crt; Type ds= ^sinhvien; sinhvien=record Mhs: Byte; Hoten:string[25]; Diem:real; next:ds; End; Var dsl, ctdau, ctcuoi:ds; bd:pointer; lam:char; i,j:byte; Procedure Hien_FIFO; {Hien du lieu tu dau xuong cuoi} Var ct1:ds; Begin Clrscr; writeln('Du lieu da nhap - Hien tu dau xuong cuoi'); writeln; ct1:=ctdau; while ct1nil do with ct1^ do Begin Write(Mhs,' ',hoten); for j:=1 to (20-length(hoten)) do write(' '); writeln(diem:5:2); ct1:=next; End; End; Begin {Than chuong trinh chinh} clrscr; mark(bd); ctdau:=nil; i:=0; Repeat New(dsl); {tạo biến động Dsl} i:=i+1; With dsl^ do Begin Witeln('Ma ho so so: ',i); mhs:=i; Write('Ho va ten: '); readln(hoten); {nhập dữ liệu} Write('Diem : '); Readln(diem); if ctdau=nil then ctdau:=dsl {ctdau trỏ vào phần tử thứ 1 } else ctcuoi^.next:=dsl; {Lưu trữ địa chỉ của phần tử hiện thời - kể từ phần tử thứ hai vào trường liên kết Next của Ctcuoi } ctcuoi:=dsl; {ghi nhận lại con trỏ cuối, nghĩa là Next đang trỏ vào phần tử hiện thời} ctcuoi^.next:=nil; { trường liên kết Next của con trỏ cuối trỏ vào Nil - ket thuc danh sach} Writeln('Nhap next hay thoi? C/K '); lam:=readkey; writeln; End; Until lam in ['k','K']; Hien_FiFO; Release(bd); End. 2.4. Các thao tác trên danh sách a, Chèn thêm phần tử vào danh sách Việc đầu tiên khi muốn chèn thêm phần tử vào danh sách là phải xác định được chính xác vị trí cần chèn, muốn vậy danh sách phải có một trường khoá, mỗi phần tử trong danh sách sẽ ứng với một và chỉ một giá trị của trường khoá. Ví dụ có thể lấy trường số thứ tự (STT) hoặc mã hồ sơ (MHS) làm trường khoá, không thể dùng trường Hoten để làm khoá vì trong danh sách có thể có nhiều người trùng Hoten. Quá trình chèn một phần tử vào danh sách sẽ qua các bước: * Xác định vị trí chèn * Tạo một biến động (xem như một con trỏ nháp) và xin cấp phát vùng nhớ cho biến động để lưu dữ liệu sẽ chèn vào danh sách. * Chuyển trường Next của phần tử hiện thời đến phần tử bổ xung * Chuyển trường Next của phần tử bổ xung đến phần tử trước phần tử hiện thời. New(ct1); With ct1^ do Nhập dữ liệu cho phần phần tử bổ xung dsl:=ctcuoi; While (dslnil) and (dsl^.mhs n) do dsl:=dsl^.next; ct1^.next:=dsl^.next dsl^.next:=ct1; b, Xoá một phần tử khỏi danh sách Quá trình xoá một phần tử trong danh sách không phải là quá trình làm rỗng ô nhớ chứa phần tử mà đơn giản chỉ là chuyển hướng trường liên kết không trỏ vào phần tử đó nữa. Nếu chúng ta xoá nhiều phần tử thì bộ nhớ sẽ bị phân mảnh ra nhiều đoạn ngắt quãng, trong trường hợp này cần bố trí lại các ô nhớ để một đối tượng được bố trí trên một số ô nhớ liên tục 3. Tạo thư viện các chương trình con (UNIT) 3.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; 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. 3.2. Cấu trúc một Unit Cấu trúc một UNIT bao gồm bốn phần cơ bản sau đây: a, 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, tên unit cần có tính chất gợi nhớ, không nên đặt tên quá dài . b, 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, 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. Chú ý: 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. d, 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. 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ụ 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; 4. Đồ họa và âm thanh 4.1. Đồ họa a. Chế độ đồ hoạ (Graphic) Unit Graph (Graph.tpu) chứa trên 50 chương trình con liên quan đến đồ hoạ. Để sử dụng chế độ đồ hoạ (Graphic Mode), cần có các tệp sau: \TP\UNITs\Graph.tpu \TP\BGI\*.bgi nhằm tương thích phần cứng và *.chr hỗ trợ cho phông chữ. Trong chế độ đồ hoạ, màn hình được chia thành các điểm (Pixel). Mỗi điểm được xác định bởi cặp toạ độ (x,y). Điểm (0,0) là ứng với góc trên bên trái, điểm (GetMaxX, GetMaxY) ứng với góc dưới bên phải màn hình. Giá trị của GetMaxX và GetMaxY phụ thuộc vào Mode khởi tạo (InitGraph). Với các thẻ điều hợp hiển thị hiên nay, ta sử dụng tệp EGAVGA.BGI và phó mặc việc lựa chọn Mode cho chương trình thì nhận được độ phân giải là 640x480. b. Cốt lõi của chương trình đồ hoạ Chương trình sử dụng chế độ đồ hoạ sẽ có dạng: uses Graph; var grDriver, grMode, ErrCode: Integer; begin grDriver:= DETECT; InitGraph(grDriver, grMode, ); ErrCode:= GraphResult; if ErrCode = grOk then begin { Do graphics. Thực hiện các thao tác lập trình về đồ hoạ} CloseGraph end else Writeln('Graphics error:', GraphErrorMsg(ErrCode)) end. Trong đó, DETECT là hằng, biểu thị (chỉ thị cho chương trình) đường dẫn tới tệp EGAVGA.BGI, GraphResult là hàm báo trạng thái khởi tạo, hằng grOK thực chất là bằng 0. Hàm GraphErrorMsg(ErrCode: Integer) là xâu tương ứng với mã lỗi ErrCode, Thủ tục CloseGraph sẽ đóng chế độ đồ hoạ và trở về chế độ TEXT. Chú ý: nếu chỉ vẽ hình thì trước câu lệnh CloseGraph, phải có lệnh dừng màn hình để có thể quan sát hình vẽ. 4.2. Các đối tượng đồ họa Có thể tạm thời phân các đối tượng trong đồ hoạ thành 3 nhóm: Nhóm 1: Văn bản (TEXT) Nhóm 2: Điểm (Pixel), đoạn thẳng (Line), đường chữ nhật (Rectangle), đa giác (Poly), đường tròn (Circle), đường cung tròn (Arc), đường Ellipse. Nhóm 3 (có tô màu): Hình chữ nhật (Bar), hình Ellipse, hình quạt (Sector), quạt tròn (PiesLice), hình hộp (Bar3D). Các đối tượng thuộc nhóm 1 và 2 không có phương thức tô màu (Fill). 4.3. Các thủ tục và hàm liên quan đến Đặt - nhận màu - nét và dáng điệu chữ. GetMaxX: Integer: Giá trị cực đại của toạ độ X GetMaxY: Integer: Giá trị cực đại của toạ độ Y GetX: Integer: Giá trị toạ độ X hiện thời của con trỏ màn hình (không hiện). GetY: Integer: Giá trị toạ độ Y hiện thời của con trỏ màn hình (không hiện). GetPixel(X,Y: Integer):Word: Giá trị màu của điểm (X,Y). TextHeight(TextString: string):Word: Giá trị chiều cao (Pixel) của TextString. TextWidth(TextString: string): Word: Giá trị chiều rộng (Pixel) của TextString. SetColor(Color: Word): Đặt màu(Color) cho nét vẽ, có hiệu lực cho đến khi đặt lại. GetColor: Word: Hàm nhận lại màu nét vẽ đang được đặt. GetMaxColor: Word: Giá trị cực đại của màu. SetBkColor(ColorNum: Word): Đặt màu nền ColorNum cho các đối tượng. GetBkColor: Word: Hàm nhận lại màu nền đang được đặt. SetLineStyle(LineStyle: Word; Pattern: Word; Thickness: Word): Đặt kiểu dáng (Style), mẫu (Pattern) và độ dày (Thickness) cho đường. LineStyle: SolidLn 0 Đường liền nét DottedLn 1 Đường chấm chấm CenterLn 2 Đường gạch chấm Dashed Ln 3 Đường gạch gạch UserBitLn 4 Đường người dùng định nghĩa Thickness: NormWidth 1 Một nét ThickWidth 3 Ba nét Pattern: Chỉ có nghĩa khi sử dụng UserBitLn. Pattern được định nghĩa bởi hai byte. Ví dụ: nếu Pattern = $EBAC thì với NormWidth sẽ là 1110 1011 1010 1100, còn với ThickWidth sẽ là 1110 1011 1010 1100 1110 1011 1010 1100 1110 1011 1010 1100 GetLineSettings(var LineInfo: LineSettingsType) Nhận lại sự đặt của SetLineStyle trước đó. LineSettingsType là bản ghi gồm ba trường là: LineStyle, Pattern, Thickness: Word; SetTextStyle(Font, Direction: Word; CharSize: Word): Đặt lại phông (Font) chữ. Font: phông (Font) chữ DefaultFont 0 8x8 bit mapped font TriplexFont 1 Stroked font SmallFont 2 Stroked font SansSerifFont 3 Stroked font GothicFont 4 Stroked font Direction: Hướng trái sang phải hoặc hướng từ dưới lên. HorizDir 0 Orient left to right VertDir 1 Orient bottom to top CharSize: Cỡ chữ là 1, 2, 3, .. tuỳ theo loại font. SetTextJustify(Horiz, Vert: Word): Đặt kiểu chữ Horiz: (Horizontal) Vert: Theo chiều dọc (Vertical) LeftText 0 BottomText 0 CenterText 1 CenterText 1 RightText 2 TopText 2 GetTextSettings(var TextInfo: TextSettingsType): Nhận lại các giá trị đã đặt bởi hai thủ tục SetTextStyle vµ SetTextJustify. TextSettingsType là bản ghi gồm các trường: Font, Direction, CharSize, Horiz, Vert: Word. SetFillStyle(Pattern: Word; Color: Word): Đặt màu tô (Color) và kiểu tô (Pattern). GetFillSettings(var FillInfo: FillSettingsType): Nhận sự đặt lại của SetFillStyle. Bản ghi FillSettingsType có hai trường là:Pattern,Color: Word. SetViewPort(x1,y1,x2,y2:Integer;Clip:Boolean): Định nghĩa cửa sổ đưa ra. Định nghĩa ViewPort tức là định nghĩa một vùng hình chữ nhật trên màn hình, (x1,y1) và (x2, y2) tương ứng là các toạ độ của góc trên bên trái và góc dưới bên phải, để từ đó trở đi (cho tới khi đặt lại), (x1, y1) được coi là (0, 0) trong cửa sổ đó. Tham số Clip bằng True thì các phần hình vẽ bên ngoài cửa sổ sẽ không hiện ra, bằng False thì hiện ra. GetViewSettings(var ViewPort: ViewPortType): Nhận lại các giá trị được đặt bởi SetViewPort. ViewPortType là bản ghi gồm năm trường: x1, y1, x2, y2: Integer vµ Clip: Boolean. CÁCH THỂ HIỆN CÁC ĐỐI TƯỢNG PutPixel(X, Y: Integer; Color: Word): Vẽ điểm màu Color tại (X, Y). MoveTo(X, Y: Integer): Đặt con trỏ màn hình vào điểm (X, Y). MoveRel(Dx, Dy: Integer): Dịch con trỏ với độ lệch (Dx, Dy) so với vị trí hiện tại. OutText(TextString: string): Hiển thị xâu TextString tại điểm hiện thời. OutTextXY(X, Y: Integer,TextString: string): Hiển thị xâu TextString tại điểm (X, Y). LineTo(X, Y: Integer): Vẽ một đoạn thẳng từ điểm hiện tại đến điểm (X, Y). LineRel(Dx, Dy: Integer): Vẽ một đoạn thẳng từ điểm hiện tại đến điểm theo độ lệch (Dx, Dy). Line(x1, y1, x2, y2: Integer): Vẽ một đoạn thẳng từ điểm (x1, y1) đến điểm (x2, y2). Rectangle(x1, y1, x2, y2: Integer): Vẽ đường chữ nhật có toạ độ hai đỉnh đối diện: (x1, y1, x2, y2). Circle(X,Y: Integer; Radius: Word): Vẽ đường tròn tâm (X, Y), bán kính bằng Radius. Arc (X,Y; Integer; StAngle, EndAngle, Radius; Word): Vẽ cung tròn tâm (X, Y), góc bắt đầu là StAngle (độ), góc kết thúc là EndAngle (độ) , bán kính bằng Radius. Ellipse(X, Y: Integer; StAngle, EndAngle: Word; XRadius, YRadius: Word): Vẽ Ellipse tâm (X, Y), góc bắt đầu là StAngle (độ), góc kết thúc là EndAngle (độ) , các bán trục tương ứng là XRadius và YRadius. DrawPoly(NumPoints: Word; var PolyPoints): Vẽ đa giác PolyPoints với số đỉnh là NumPoints. Ví dụ như sau: const Triangle: array[1..4] of PointType = ((X: 50; Y: 100), (X: 100; Y:100), (X: 150; Y: 150), (X: 50; Y: 100)); DrawPoly(SizeOf(Triangle) div SizeOf(PointType), Triangle); PointType là bản ghi gồm hai trường X, Y: Integer, nên SizeOf(PointType) bằng 3. Vì Triangle có số byte là 4*4=16 (byte) nên SizeOf(Triangle) div SizeOf(PointType) bằng 3. Bar(x1, y1, x2, y2: Integer): Hình chữ nhật (x1, y1, x2, y2). Bar3D(x1, y1, x2, y2: Integer; Depth: Word; Top: Boolean): Hình hộp, chiều sâu là Depth. Nếu Top=TopOn (TRUE) thì hộp có nắp, bằng TopOff (FALSE) là không có nắp. FillEllipse(X, Y: Integer; XRadius, YRadius: Word): Vẽ hình Elippse kín và có tô màu. Sector(x, y: Integer; StAngle,EndAngle, XRadius, YRadius: Word): Hình quạt, có tô. PieSlice(X, Y: Integer; StAngle, EndAngle, Radius: Word): Hình quạt tròn, có tô. FillPoly(NumPoints: Word; var PolyPoints): Đa giác, có tô. Ví dụ: const Triangle: array[1..3] of PointType=((X: 50; Y: 100),(X: 100; Y: 100), (X: 150; Y: 150)) FillPoly(SizeOf(Triangle) div SizeOf(PointType), Triangle). FloodFill(X, Y: Integer; Border: Word): Tô màu hình vẽ theo kiểu loang dần, bắt đầu tại điểm (X,Y) những nếu gặp màu cạnh viền (Boder) thì không tô. 4.4. Một số thủ tục khác GetGraphMode: Integer: Nhận lại Mode đồ hoạ SetGraphMode(Mode: Integer): Đặt Mode đồ hoạ. RestoreCrtMode: Trở về chế độ văn bản. DirectVideo(Biến): Mặc định là True: không dùng được các thủ Read, Write. ClearViewPort: Xoá hình vẽ bên trong ViewPort ClearDevice: Xoá toàn màn hình ImageSize(x1,y1,x2,y2: Integer):Word: Số Byte cần thiết để lưu vùng màn hình (x1,y1,x2,y2) vào bộ nhớ RAM. GetMem(var P: Pointer; Size: Word): Xin cấp phát Size byte cho con trỏ P. GetImage(x1, y1, x2, y2: Integer; var BitMap): Lưu vùng (x1, y1, x2, y2) vào bộ nhớ RAM và cho con trỏ (không kiểu) BitMap trỏ tới. PutImage(X, Y: Integer; var BitMap; BitBlt: Word): Đưa ảnh lưu trong RAM đang được trỏ bởi con trỏ BitMap lên màn hình, bắt đầu tại điểm (X,Y). Kiểu đưa ra là BitBlt: NormalPut 0 MOV OrPut 2 OR CopyPut 0 MOV AndPut 3 AND XORPut 1 XOR NotPut 4 NOT Ví dụ: uses Graph; var Gd, Gm: Integer; P: Pointer; Size: Word; begin Gd:= Detect; InitGraph(Gd, Gm, ' '); if GraphResult grOk then Halt(1); Bar(0, 0, GetMaxX, GetMaxY); Size:= ImageSize(10, 20, 30, 40); GetMem(P, Size); { Allocate memory on heap } GetImage(10, 20, 30, 40, P^); Readln; ClearDevice; PutImage(100, 100, P^, NormalPut); Readln; CloseGraph end. 5. Một số bài toán ứng dụng 5.1. Sắp xếp Để tiện trình bày, ta coi n £ 100, giá trị các phần tử A[i] của mảng A có kiểu Integer. Ta định nghĩa các hằng số Max và kiểu Vec (Vector) như sau: const Max=100; type Vec=array[1..Max] of Integer; Ta định nghĩa một thủ tục hoán đổi giá trị: procedure Swap(var x, y: Integer); begin x:=x+y; y:=x-y; x:=x-y end; Các thủ tục sau đều sử dụng kiểu Vec. Hơn nữa, tham số hình thức Increment được đưa vào nhằm chỉ định chiều của sự sắp xếp. Increment bằng True (hoặc False) tương ứng với sắp xếp theo chiều tăng (hoặc giảm). a. Sắp xếp chọn procedure SelectSort(var A: Vec; n: Byte; Increment: Boolean); var i, j, k: byte; begin for i:=1 to n-1 do begin j:=i; for k:=i+1 to n do if (A[k]<A[j])= Increment then j:=k; if j>i then Swap(A[i], A[j]) end end; b. Sắp xếp chèn(Insertion sort) procedure InsertSort(var A: Vec; n: Byte; Increment: Boolean); var i, j: byte; Temp: Integer; begin for i:=2 to n do if (A[i]<A[i-1])= Increment then begin j:=i; Temp:=A[i]; while (j>1) and ((Temp <A[j-1])=Increment) do begin A[j]:=A[j-1]; j:=j-1 end; A[j]:=Temp end end; c. Sắp xếp nổi bọt (Bubble sort) procedure BubbleSort(var A: Vec; n: Byte; Increment: Boolean); var i, j: byte; begin for i:=1 to n-1 do for j:=n downto i+1 do if (A[j]<A[j-1])= Increment then Swap(A[j-1], A[j]) end; Nhận xét: Trong thuật toán sắp xếp nổi bọt, rất có thể ứng với giá trị i nào đó nhỏ hơn n-1, một lượt duyệt lùi của j từ n đến i+1 các phần tử không đổi chỗ lần nào. Điều đó chứng tỏ mảng đã đúng thứ tự, nên không cần xét với các bước i lớn hơn nữa. Vì vậy, ta có thể cải tiến thuật toán trên như sau: procedure BubbleSort(var A: Vec; n: Byte; Increment: Boolean); var i, j: byte; Temp: Integer; OK: boolean; begin OK:=False; i:=1; while (i<n) and (not OK) do begin OK:=True; for j:=n downto i+1 do if (A[j]<A[j-1])= Increment then begin Swap(A[j-1], A[j]); OK:= False end; i:=i+1 end end; d. Sắp xếp kiểu hoà nhập (Merge sort). * Đặt vấn đề: Hoà nhập là phép hợp nhất hai mảng (cùng kiểu giá trị) đã được sắp xếp cùng chiều thành một mảng thứ ba cũng được sắp xếp theo chiều đó. Giả sử có hai mảng A và B với số phần tử tương ứng là m và n, ta hợp nhất thành mảng V. *Thuật toán: Giả sử hai mảng A và B đã được sắp xếp không giảm. Xét từ đầu hai mảng A và B, nếu giá trị của phần tử đang xét của mảng A nhỏ hơn giá trị đang xét của mảng B thì phần tử mới của mảng C sẽ là phần tử đang xét của mảng A, ngược lại lấy phần tử đang xét của mảng B. Khi một trong hai mảng đã được xét hết, thì các phần tử còn lại của mảng kia sẽ lần lượt được nạp hết vào cuối mảng C. Ta định nghĩa mảng C với số phần tử bằng tổng số phần tử của cả A và B. type Vec2=array[1..2*Max] of integer; procedure MergeSort(A, B: Vec; var C: Vec2; m, n:byte); var i, j, k: byte; begin i:=1; j:=1; k:=0; while (i<m) and (j<n) do if A[i]<B[j] then begin k:=k+1; C[k]:=A[i]; i:=i+1 end else begin k:=k+1; C[k]:=B[j]; j:=j+1 end; if i>m then while j<=n do begin k:=k+1; C[k]:=B[j]; j:=j+1 end else while i<=m do begin k:=k+1; C[k]:=A[i]; i:=i+1 end end; Độ phức tạp của thuật toán là O(m+n). 5.2 Tìm kiếm a. Tìm kiếm tuần tự (sequential searching) function SequenSearch(A: Vec; n:byte; X: integer):byte; var k:byte; begin k:= 1; while (kX) do k:=k+1; if k > n then SequenSearch:=0 end; b. Tìm kiếm nhị phân (Binary searching) function Search(A: vec; left, right: byte; x: integer):byte; var midle: byte; begin if left > right then begin Search:=0; exit end else begin midle:=(left+right) div 2; if A[midle]= x then begin Search:=midle; exit end else if x < A[midle] then Search:= Search(A, left, midle-1, x) else Search:= Search(A, midle+1, right, x) end end; function BinarySearch(A: vec; n: byte; x: integer):byte; begin BinarySearch:= Search(A, 1, n, x) end; 5.3 Biến đổi ma trận Const nMax=10; type Vec=array[1..nMax] of real; Mat=array[1..nMax,1..nMax+1] of real; a. Nhập mảng một chiều procedure VecInput(var A: Vec; n: byte; Name: Char); var k: byte; begin for k:=1 to n do begin write(Name, '[', k, ']= '); readln(A[k]) end end; b. Tính tích vô hướng function ScalarProduct(A, B: Vec; n: byte): real; var k: byte; Result: real; begin Result:=0; for k:=1 to n do Result:= Result + A[k]*B[k]; ScalarProduct:=Result end; c. Nhập mảng hai chiều procedure MatInput(var A: Mat; m, n: byte; Name: Char); var i,j: byte; begin writeln('Nhap Ma tran ', Name, ':'); for i:=1 to m do for j:=1 to n do begin write(Name, '[',i, ',', j,']= '); readln(A[i, j]) end end; d. Hiển thị mảng hai chiều procedure MatDisplay(A: Mat; m, n: byte; Name: Char); var i,j: byte; begin writeln('Ma tran ', Name, ' la:'); for i:=1 to m do begin for j:=1 to n do write(A[i, j]:10:4); writeln end end; e. Hiển thị ma trận tam giác trên procedure MatAboveDisplay(A: Mat; n: byte); var i,j: byte; begin for i:=1 to n do begin for j:=1 to n do if i <= j then write(A[i, j]:8:1) else write(#32:8); writeln end end; f. Tính tổng hai ma trận. procedure MatSum(A, B: Mat; var C: Mat; m, n: byte); var i, j, k: byte; begin for i:=1 to m do for j:=1 to n do C[i, j]:= A[i, j] + B[i, j] end; g. Tính tích hai ma trận. procedure MatProduct(A, B: Mat; var C: Mat; m, p, n: byte); var i, j, k: byte; begin for i:=1 to m do for j:=1 to n do begin C[i, j]:= 0; for k:= 1 to p do C[i, j]:= C[i, j] + A[i, k]*B[k, j] end end; h.Tìm phần tử lớn nhất trên cột Col, kể từ hàng Col đến hàng n của ma trận A. function MaxRow(A: Mat; Col,n: byte): byte; var i, k: byte; begin k:=Col; for i:= Col + 1 to n do if abs(A[i, Col]) > abs(A[k, Col]) then k:=i; MaxRow:= k end; i. Hoán đổi x với y. procedure Swap(var x, y: real); var t: real; begin t:=x; x:=y; y:=t end; j. Hoán đổi hàng p với hàng r, kể từ cột thứ p của ma trận A procedure RowSwap(var A: Mat; p, r, n: byte); var j: byte; begin for j:=p to n do Swap(A[p,j], A[r,j]) end; k. Đưa ma trận A về dạng tam giác trên trên theo phương pháp Gausse. function AboveConvert(var A: Mat; n: byte; var Sign: shortint): boolean; var i,j, k, p: byte; t: real; Stop: boolean; begin k:=0; Sign:=1; Stop:= False; repeat k:=k+1; p:= MaxRow(A, k, n); if p > k then begin RowSwap(A, k, p, n+1); Sign:=-Sign end; if A[k,k] 0 then for i:=k+1 to n do begin t:=A[i, k]/A[k, k]; for j:=k to n+1 do A[i, j]:= A[i,j] - t*A[k,j] end else Stop:=True until (k=n-1) or Stop; AboveConvert:= not Stop end; l. Tính định thức của ma trận bằng phương pháp Gausse. function Determinal(var A: Mat; n: byte): real; var k: byte; t: real; Sign: Shortint; begin if AboveConvert(A,n, Sign) then begin t:=Sign; for k:=1 to n do t:=t*A[k,k]; Determinal:=t end else Determinal:=0 end; m. Giải hệ n phương trình tuyến tính n ẩn. procedure Solve(var A: Mat; n: byte); var k, j: byte; t: real; begin if Determinal(A,n) 0 then begin MatDisplay(A,n,n+1,'A'); for k:= n downto 1 do begin t:=0; for j:=k+1 to n do t:= t+A[k,j]*A[j,n+1]; A[k, n+1]:= (A[k, n+1]-t)/A[k, k] end end end; TÀI LIỆU THAM KHẢO Ngôn ngữ lập trình Pascal, Quách Tuấn Ngọc, NXB Giáo dục Bài tập Pascal, Quách Tuấn Ngọc, NXB Giáo dục Lập trình nâng cao trên ngôn ngữ Pascal, Nguyễn Tô Thành, Nhà xuất bản Đai học Quốc Gia Hà Nội. Lập trình, Dự án trung học cơ sở Bài tập lập trình cơ sở Ngôn ngữ Pascal, Nguyễn Hữu Ngự, NXB Đại học Quốc Gia Hà Nội Tin học đại cương, Đại học CNTT và truyền thông.

Các file đính kèm theo tài liệu này:

  • docgiao_trinh_pascal_hoa_lu_5061.doc
Tài liệu liên quan