pascal 4

By o0osaoxao0o

1.1K 0 0

More

pascal 4

1.1K 0 0
By o0osaoxao0o

CƠ BẢN VỀ NGÔN NGỮ LẬP TRÌNH PASCAL

I. GIỚI THIỆU.

II. CÁC PHẦN TỬ CƠ BẢN CỦA NGÔN NGỮ PASCAL.

1. Bộ KÝ Tự.

2. Từ khóa.

3. Tên chuẩn.

4. Danh hiệu tự đặt.

III. CẤU TRÚC MỘT CHƯƠNG TRÌNH PASCAL.

IV. CÁC KIỂU DỮ LIỆU CƠ SỞ : INTEGER, REAL, BOOLEAN, CHAR.

1. Khái niệm.

2. Kiểu số nguyên (Integer type).

3. Kiểu số thực (Real type).

4. Kiểu logic (Boolean type).

5. Kiểu ký tự (Char type).

V. CÁC KHAI BÁO HẰNG, BIẾN, KIỂU, BIỂU THỨC, ....

1. Hằng.

2. Biến.

3. Kiểu.

4. Biểu thức.

VI. CÁC THỦ TỤC XUẤT/NHẬP.

1. Câu lệnh.

2. Cấu trúc tuần tự.

3. Cấu trúc rẽ nhánh.

4. Cấu trúc lặp.

BÀI ÐỌC THÊM NHẬP & XUẤT DỮ LIỆU TRONG TURBO PASCAL.

I. GIỚI THIỆU TOP

Pascal là tên của một trong các ngôn ngữ lập trình cấp cao thông dụng. Ngôn ngữ lập trình Pascal được giáo sư Niklaus Wirth ở trường Ðại học Kỹ thuật Zurich (Thụy sĩ) thiết kế và công bố vào năm 1970. Niklaus Wirth đặt tên cho ngôn ngữ này là Pascal để tưởng nhớ đến nhà Toán học và Triết học Pháp ở thế kỷ 17 là Blaise Pascal, người đã phát minh ra một máy tính cơ khí đơn giản đầu tiên của con người.

Ngôn ngữ Pascal được dùng hiện nay có nhiều điểm khác biệt với chuẩn Pascal nguyên thủy của Giáo sư Wirth. Tùy theo quốc gia hoặc công ty đã phát triển cho ra đời các chương trình biên dịch ngôn ngữ Pascal như:

• ISO PASCAL (International Standards Organization) của Châu Âu

• ANSI PASCAL (American National Standards Institute) của Mỹ

• TURBO PASCAL của hãng BORLAND (Mỹ)

• IBM PASCAL của hãng Microsoft (Mỹ)

• v.v...

Ðến nay, ngôn ngữ Pascal đã phát triển đến phiên bản Turbo Pascal Version 7. Các diễn giải và ví dụ trong giáo trình này chủ yếu sử dụng chương trình Turbo Pascal 5.5 - 7.0, hiện đang được sử dụng rộng rãi ở Việt Nam.

II. CÁC PHẦN TỬ CƠ BẢN CỦA NGÔN NGỮ PASCAL

1. Bộ ký tự

- Bộ 26 chữ Latin:

Chữ in: A, B, C, ..., X, Y, Z

Chữ thường: a, b, c, ..., x, y, z

- Bộ chữ số thập phân: 0, 1, 2, 3, ..., 8, 9

- Ký tự gạch nối dưới: _

- Các ký hiệu toán học: +, -, *, /, =, <, >, (, ), [, }

2. Từ khóa TOP

Là các từ riêng của Pascal, có ngữ nghĩa đã được xác định, không được dùng nó vào các việc khác hoặc đặt tên mới trùng với các từ khóa.

- Từ khóa chung:

PROGRAM, BEGIN, END, PROCEDURE, FUNCTION

- Từ khóa để khai báo:

CONST, VAR, TYPE, ARRAY, STRING, RECORD, SET, FILE, LABEL

- Từ khóa của lệnh lựa chọn:

IF ... THEN ... ELSE, CASE ... OF

- Từ khóa của lệnh lặp:

FOR... TO... DO, FOR... DOWNTO... DO, WHILE... DO,

REPEAT... UNTIL

- Từ khóa điều khiển:

WITH, GOTO, EXIT, HALT

- Từ khóa toán tử:

AND, OR, NOT, IN, DIV, MOD

3. Tên chuẩn TOP

Tên chuẩn là tên đã được định nghĩa sẵn trong Pascal, nhưng người ta có thể định nghĩa lại nếu muốn. Trong Pascal ta có các tên chuẩn sau đây:

Boolean, Char, Integer, Word, Byte, Real, Text

False, True, MaxInt

Abs, Arctan, Chr, Cos, Sin, Eof, Eoln

Exp, Ln, Odd, Ord

Round, Trunc, Sqr, Pred, Succ

Dispose, New, Get, Put, Read, Readln,

Write, Writeln

Reset, Rewrite

4. Danh hiệu tự đặt

Trong Pascal để đặt tên cho các biến, hằng, kiểu, chương trình con ta dùng các danh hiệu (identifier). Danh hiệu của Pascal được bắt đầu bằng một chữ cái, sau đó có thể là các chữ cái, chữ số hay là dấu nối, không được có khoảng trắng và độ dài tối đa cho phép là 127.

Ví dụ 6.1: Sau đây là các danh hiệu: x; S1; Delta; PT_bac_2

Pascal không phân biệt chữ thường và chữ hoa trong một danh hiệu.

Ví dụ 6.2: aa và AA là một; XyZ_aBc và xyZ_AbC là một

Khi viết chương trình ta nên đặt các danh hiệu sao cho chúng nói lên các ý nghĩa của đối tượng mà chúng biểu thị. Ðiều này giúp chúng ta viết chương trình dễ dàng và người khác cũng dễ hiểu nội dung chương trình.

III. CẤU TRÚC MỘT CHƯƠNG TRÌNH PASCAL TOP

Hình 6.1: Sơ đồ cấu trúc chương trình Pascal

Ví dụ 6.3:

PROGRAM Hello; { Dòng tiêu đề }

USES Crt; { Lời gọi sử dụng các đơn vị chương trình }

VAR Name : string; { Khai báo biến }

PROCEDURE Input; { Có thể có nhiều Procedure và Function }

Begin

ClrScr; { Lệnh xóa màn hình }

Write(' 'Hello ! What is your name ?... '');Readln(Name);

End;

BEGIN { Thân chương trình chính }

Input;

Writeln (' 'Welcome to you, ', Name') ;

Writeln (' 'Today, we study PASCAL PROGRAMMING ... '');

Readln;

END.

Một chương trình Pascal có các phần:

* Phần tiêu đề:

Phần này bắt đầu bằng từ khóa Program rồi tiếp đến là tên của chương trình và chấm dứt bằng dấu chấm phẩy (;)

Tên chương trình phải được đặt theo đúng qui cách của danh hiệu tự đặt. Phần tiêu đề có hay không cũng được.

* Phần khai báo dữ liệu:

Trước khi sử dụng biến nào phải khai báo biến đó, nghĩa là xác định rõ xem biến đó thuộc kiểu dữ liệu nào. Một chương trình Pascal có thể có một số hoặc tất cả các khai báo dữ liệu sau:

CONST : khai báo hằng

...

TYPE : định nghĩa kiểu dữ liệu mới

...

VAR : khai báo các biến

...

* Phần khai báo chương trình con:

Phần này mô tả một nhóm lệnh được đặt tên chung là một chương trình con để khi thân chương trình chính gọi đến thì cả nhóm lệnh đó được thi hành.

Phần này có thể có hoặc không tùy theo nhu cầu.

* Phần thân chương trình:

Phần thân chương trình là phần quan trọng nhất và bắt buộc phải có, phần này luôn nằm giữa 2 từ khoá là BEGIN và END. Ở giữa là lệnh mà các chương trình chính cần thực hiện. Sau từ khóa END là dấu chấm (.) để báo kết thúc chương trình.

* Dấu chấm phẩy (;):

Dấu ; dùng để ngăn cách các câu lệnh của Pascal và không thể thiếu được.

* Lời chú thích:

Lời chú thích dùng để chú giải cho người sử dụng chương trình nhớ nhằm trao đổi thông tin giữa người và người, máy tính sẽ không để ý đến lời chú thích này. Lời chú thích nằm giữa ký hiệu: { } hoặc (* *)

IV. CÁC KIỂU DỮ LIỆU CƠ SỞ: INTEGER, REAL, BOOLEAN, CHAR

1. Khái niệm TOP

Dữ liệu (data) là tất cả những gì mà máy tính phải xử lý.

Theo Niklaus Wirth:

CHƯƠNG TRÌNH = THUẬT TOÁN + CẤU TRÚC DỮ LIỆU

Một kiểu dữ liệu (data type) là một qui định về hình dạng, cấu trúc và giá trị của dữ liệu cũng như cách biểu diễn và cách xử lý dữ liệu.

Trong Pascal các kiểu dữ liệu gồm các loại sau:

- Kiểu đơn giản (Simple type): bao gồm kiểu số nguyên (Integer), kiểu số thực (Real), kiểu logic (Boolean), kiểu ký tự (Char).

- Kiểu có cấu trúc (Structure type): bao gồm mảng (Array), chuỗi (String), bản ghi (Record), tập hợp (Set), tập tin (File).

- Kiểu chỉ điểm (pointer):

Trong chương này, chúng ta chỉ xét các kiểu dữ liệu đơn giản.

2. Kiểu số nguyên (Integer type) TOP

a. Kiểu số nguyên thuộc Z chứa trong Turbo Pascal

Ðược định nghĩa với các từ khóa sau:

TỪ KHÓA SỐ BYTE PHẠM VI

BYTE 1 0 .. 255

SHORTINT 1 - 128 .. 127

INTEGER 2 - 32768 .. + 32767

WORD 2 0 .. 65535

LONGINT 4 - 2147483648 .. 2147483647

b. Các phép toán số học đối với số nguyên

KÝ HIỆU Ý NGHĨA

+ Cộng

- Trừ

* Nhân

/ Chia cho kết quả là số thực

DIV Chia lấy phần nguyên

MOD Chia lấy phần dư

SUCC (n) n + 1

PRED (n) n - 1

ODD (n) TRUE nếu n lẻ

và FALSE nếu n chẵn

3. Kiểu số thực (Real type) TOP

Ở Turbo Pascal, kiểu số thực thuộc tập hợp R chứa trong 6 bytes, được định nghĩa với từ khóa REAL: R =([2.9 x 10-39 , 1.7 x 1038 ]

Hay viết theo dạng số khoa học: R = ( [2.9E-39, 1.7E38]

Số thực có thể viết theo kiểu có dấu chấm thập phân bình thường hoặc viết theo kiểu thập phân có phần mũ và phần định trị.

Các phép toán số học cơ bản +, -, * , /dĩ nhiên được sử dụng trong kiểu real.

Bảng dưới đây là các hàm số học cho kiểu số thực:

KÝ HIỆU Ý NGHĨA

ABS (x) |x| : lấy giá trị tuyệt đối của số x

SQR (x) x2 : lấy bình phương trị số x

SQRT(x) : láúy càn báûc 2 cuía trë säú x

SIN(x) sin (x) : lấy sin của x

COS (x) cos (x) : lấy cos của x

ARCTAN (x) arctang (x)

LN (x) ln x : lấy logarit nepe của trị x (e ( 2.71828)

EXP (x) ex

TRUNC (x) lấy phần nguyên lớn nhất không vượt quá trị số x

ROUND (x) làm tròn giá trị của x, lấy số nguyên gần x nhất

4. Kiểu logic (Boolean) TOP

Một dữ liệu thuộc kiểu BOOLEAN là một đại lượng được chứa trong 1 byte ở Turbo Pascal và chỉ có thể nhận được một trong hai gía trị logic là TRUE (đúng) và FALSE (sai).

Qui ước: TRUE > FALSE

Các phép toán trên kiểu Boolean:

A B NOT A A AND B A OR B A XOR B

TRUE TRUE FALSE TRUE TRUE FALSE

TRUE FALSE FALSE FALSE TRUE TRUE

FALSE TRUE TRUE FALSE TRUE TRUE

FALSE FALSE TRUE FALSE FALSE FALSE

Nhận xét:

• Phép AND (và) chỉ cho kết quả là TRUE khi cả 2 toán hạng là TRUE

• Phép OR (hoặc) chỉ cho kết quả là FALSE khi cả 2 toán hạng là FALSE

• Phép XOR (hoặc triệt tiêu) luôn cho kết quả là TRUE khi cả 2 toán hạng là khác nhau và ngược lại.

Các phép toán quan hệ cho kết quả kiểu Boolean:

KÝ HIỆU Ý NGHĨA

< > khác nhau

= bằng nhau

> lớn hơn

< nhỏ hơn

> = lớn hơn hoặc bằng

< = nhỏ hơn hoặc bằng

5. Kiểu ký tự (Char type) TOP

Tất cả các dữ liệu viết ở dạng chữ ký tự được khai báo bởi từ khóa CHAR.

Một ký tự được viết trong hai dấu nháy đơn ( ). Ðể tiện trao đổi thông tin cần phải sắp xếp, đánh số các ký tự, mỗi cách sắp xếp như vậy gọi là bảng mã. Bảng mã thông dụng hiện nay là bảng mã ASCII (xem lại chương 3).

Ðể thực hiện các phép toán số học và so sánh, ta dựa vào giá trị số thứ tự mã ASCII của từng ký tự, chẳng hạn: 'A' < 'a' vì số thứ tự mã ASCII tương ứng là 65 và 97.

Trong Turbo Pascal mỗi ký tự được chứa trong 1 byte.

Các hàm chuẩn liên quan đến kiểu ký tự:

KÝ HIỆU Ý NGHĨA

ORD(x) Cho số thứ tự của ký tự x trong bảng mã

CHR(n) hay #n Cho ký tự có số thứ tự là n

PRED(x) Cho ký tự đứng trước x

SUCC(x) Cho ký tự đứng sau x

V. CÁC KHAI BÁO HẰNG, BIẾN, KIỂU, BIỂU THỨC, ...

1. Hằng (constant) TOP

a. Ðịnh nghĩa

Hằng là một đại lượng có giá trị không đổi trong quá trình chạy chương trình. Ta dùng tên hằng để chương trình được rõ ràng và dễ sửa đổi.

b. Cách khai báo

CONST

<Tên hằng> = <giá trị của hằng> ;

Ví dụ 6.4: CONST

Siso = 100;

X = 'xxx ';

2. Biến (variable) TOP

a. Ðịnh nghĩa

Biến là một cấu trúc ghi nhớ có tên (đó là tên biến hay danh hiệu của biến).

Biến ghi nhớ một dữ liệu nào đó gọi là giá trị (value) của biến. Giá trị của biến có thể được biến đổi trong thời gian sử dụng biến.

Sự truy xuất của biến nghĩa là đọc giá trị hay thay đổi giá trị của biến được thực hiện thông qua tên biến.

Ví dụ 6.5: Readln (x) ;

Writeln (x) ;

x := 9 ;

Biến là một cấu trúc ghi nhớ dữ liệu vì vậy nó phải tuân theo qui định của kiểu dữ liệu : một biến phải thuộc một kiểu dữ liệu nhất định.

b. Cách khai báo

VAR

<Tên biến> : <Kiểu biến> ;

Ví dụ 6.6: VAR

a : Real ;

b, c : Integer ;

TEN : String [20]

X : Boolean ;

Chon : Char ;

Cần khai báo các biến trước khi sử dụng chúng trong chương trình. Khai báo một biến là khai báo sự tồn tại của biến đó và cho biết nó thuộc kiểu gì.

3. Kiểu (Type) TOP

a. Ðịnh nghĩa

Ngoài các kiểu đã định sẵn, Pascal còn cho phép ta định nghĩa các kiểu dữ liệu khác từ các kiểu căn bản theo qui tắc xây dựng của Pascal.

b. Cách khai báo

TYPE

<Tên kiểu> = <Mô tả xây dựng kiểu> ;

Ví dụ 6.7:

TYPE

SoNguyen = Integer ;

Diem = Real;

Tuoi = 1 .. 100 ;

Color = (Red, Blue, Green) ;

Thu = (Sun, Mon, Tue, Wed, Thu, Fri, Sat) ;

và khi đã khai báo kiểu gì thì ta có quyền sử dụng để khai báo biến như ở ví dụ sau:

Ví dụ 6.8: VAR

i, j : SoNguyen ;

Dtb : Diem ;

T : tuoi ;

Mau : Color ;

Ngay_hoc : Thu;

4. Biểu thức (Expression) TOP

a. Ðịnh nghĩa

Một biểu thức là một công thức tính toán bao gồm các phép toán, hằng, biến, hàm và các dấu ngoặc.

Ví dụ 6.9: 5 + A * SQRT(B) / SIN(X)

(A AND B) OR C

b. Thứ tự ưu tiên

Khi tính giá trị của một biểu thức, ngôn ngữ Pascal qui ước thứ tự ưu tiên của các phép toán từ cao đến thấp như sau:

Mức ưu tiên: Các phép toán:

1. Biểu thức trong ngoặc đơn ( )

2. Phép gọi hàm

3. Not, -

4. *, /, DIV, MOD, AND

5. +, -, OR, XOR

6. =, <>, <=, >=, <, >, IN

Ví dụ 6.10: (4+5)/3 + 6 - (sin((/2)+3)*2 = (9)/3 + 6 - (1+3)*2 = 3 + 6 - 8 = 1

c. Qui ước tính thứ tự ưu tiên

Khi tính một biểu thức có 3 qui tắc về thứ tự ưu tiên như sau:

Qui tắc 1 : Các phép toán nào có ưu tiên cao hơn sẽ được tính trước.

Qui tắc 2 : Trong các phép toán có cùng thứ tự ưu tiên thì sự tính toán sẽ được thực hiện từ trái sang phải.

Qui tắc 3 : Phần trong ngoặc từ trong ra ngoài được tính toán để trở thành một giá trị đơn.

d. Kiểu của biểu thức

Là kiểu của kết quả sau khi tính biểu thức.

Ví dụ 6.11: Biểu thức sau được gọi là biểu thức Boolean:

not (('a'>'c') and ('c'>'C')) or ('B'='b') có giá trị TRUE

VI. CÁC THỦ TỤC XUẤT/NHẬP

1. Câu lệnh (statement) TOP

a. Trong một chương trình Pascal, sau phần mô tả dữ liệu là phần mô tả các câu lệnh. Các câu lệnh có nhiệm vụ xác định các công việc mà máy tính phải thực hiện để xử lý các dữ liệu đã được mô tả và khai báo.

b. Câu lệnh được chia thành câu lệnh đơn giản và câu lệnh có cấu trúc.

(xem phần bài đọc thêm)

- Câu lệnh đơn giản

+ Vào dữ liệu : Read, Readln

+ Ra dữ liệu : Write, Writeln

+ Lệnh gán : :=

+ Lời gọi chương trình con (gọi trực tiếp tên của chương trình con)

+ Xử lý tập tin : RESET, REWRITE, ASSIGN ...

- Câu lệnh có cấu trúc

+ Lệnh ghép : BEGIN .. END

+ Lệnh chọn : IF .. THEN .. ELSE

CASE .. OF .

+ Lệnh lặp : FOR .. TO .. DO

REPEAT .. UNTIL

WHILE .. DO

c. Các câu lệnh phải được ngăn cách với nhau bởi dấu chấm phẩy ( ; ) và Các câu lệnh có thể viết trên một dòng hay nhiều dòng.

2. Cấu trúc tuần tự TOP

a. Lệnh gán (Assignment statement)

Một trong các lệnh đơn giản và cơ bản nhất của Pascal là lệnh gán. Mục đích của lệnh này là gán cho một biến đã khai báo một giá trị nào đó cùng kiểu với biến.

* Cách viết:

<Tên_biến> := <biểu thức> ;

Ví dụ 6.12: Khi đã khai báo

VAR

c : Char ;

i,j : Integer ;

x, y : Real ;

p, q : Boolean ;

thì ta có thể có các phép gán sau :

c := 'A' ;

c := Chr(90) ;

i := (35+7)*2 mod 4 ;

i := i div 7 ;

x := 0.5 ;

x := i + 1 ;

q := i > 2*j +1 ;

q := not p ;

* Ý nghĩa:

Biến và các phát biểu gán là các khái niệm quan trọng của một họ các ngôn ngữ lập trình mà Pascal là một đại diện tiêu biểu. Chúng phản ánh cách thức hoạt động của máy tính hiện nay, đó là:

- Lưu trữ các giá trị khác nhau vào một ô nhớ tại những thời điểm khác nhau.

- Một quá trình tính toán có thể coi như là một quá trình làm thay đổi giá trị của một (hay một số) ô nhớ nào đó, cho đến khi đạt được giá trị cần tìm.

b. Lệnh ghép (Compound statement)

Một nhóm câu lệnh đơn được đặt giữa 2 chữ BEGIN và END sẽ tạo thành một câu lệnh ghép.

Trong Pascal ta có thể đặt các lệnh ghép con trong các lệnh ghép lớn hơn bao ngoài của nó và có thể hiểu tương tự như cấu trúc ngoặc đơn ( ) trong các biểu thức toán học.

* Sơ đồ:

Hình 6.2: Sơ đồ cấu trúc BEGIN .. END;

Ở hình minh họa trên ta dễ thấy các nhóm lệnh thành từng khối (block). Một khối lệnh bắt đầu bằng BEGIN và chấm dứt ở END; . Trong một khối lệnh cũng có thể có các khối lệnh con nằm trong nó. Một khối chương trình thường được dùng để nhóm từ 2 lệnh trở lên để tạo thành một <Công việc> của các lệnh có cấu trúc, ta có thể gặp khái niệm này trong nhiều ví dụ ở các phần sau.

3. Cấu trúc rẽ nhánh TOP

a. Lệnh IF .. THEN .. và Lệnh IF .. THEN .. ELSE..

* Lưu đồ diễn tả các lệnh và ý nghĩa cách viết:

Hình 6. 3: Lệnh IF <Ðiều kiện> THEN <Công việc>;

Hình 6. 4: Lệnh IF .. THEN .. ELSE ..;

Chú ý:

- Ðiều kiện là một biểu thức Boolean.

- Nếu <Công việc>sau THEN hoặc ELSE có nhiều hơn một lệnh thì ta phải gói lại trong BEGIN .. END;

- Toàn bộ lệnh IF .. THEN .. ELSE xem như 1 lệnh đơn.

Ví dụ 6.13: Tính căn bậc 2 của một số

PROGRAM Tinh_can_bac_hai ;

VAR

a : Real ;

BEGIN

Write ( Nhập số a = ) ;

Readln(a) ;

IF a < 0 THEN

Write (' a : 10 : 2 , là số âm nên không lấy căn được !!! ')

ELSE

Writeln (' Căn số bậc 2 của , a : 2 : 2 , la , SQRT(a) :10 : 3 ');

Writeln (' Nhấn ENTER để thoát ... ') ;

Readln; {Dừng màn hình để xem kết quả}

END.

Ghi chú:

Trong chương trình trên, a ta thấy có dạng a :m :n với ý nghĩa m là số định khoảng mà phần nguyên của a sẽ chiếm chỗ và n là khoảng cho số trị phần thập phân của a.

b. Lệnh CASE .. OF

* Lưu đồ biểu diễn:

Hình 6.5: Lưu đồ lệnh CASE .. OF

* Cách viết, ý nghĩa:

Cách viết Ý nghĩa

CASE <Biểu thức > OF Xét giá trị của biểu thức chọn

GT1 : Công việc 1 ; Nếu có giá trị 1 (GT1) thì thi hành Công việc 1

....................... ...................................

GTi : Công việc i ; Nếu có giá trị i (GT i) thì thi hành Công việc i

....................... ....................................

ELSE Công việc 0 ; Nếu không có giá trị nào thỏa thì thực hiện

Công việc 0

END;

Ghi chú:

- Lệnh CASE .. OF có thể không có ELSE

- Biểu thức chọn là kiểu rời rạc như Integer, Char, không chọn kiểu Real

- Nếu muốn ứng với nhiều giá trị khác nhau của biểu thức chọn vẫn thi hành một lệnh thì giá trị đó có thể viết trên cùng một hàng cách nhau bởi dấu phẩy (,) : Giá trị k1, k2, ..., kp : Lệnh k ;

Ví dụ 6.14: PROGRAM Chon_mau ;

VAR color : char ;

BEGIN

write (' Chọn màu theo một trong 3 ký tự đầu là R / W / B ') ;

readln ( color) ;

CASE color OF

'R' ,'r' : write (' RED = màu đỏ ') ;

'W', 'w' : write (' WHITE = màu trắng ') ;

'B' , 'b' : write (' BLUE = màu xanh dương ') ;

END ;

Readln;

END.

4. Cấu trúc lặp TOP

a. Lệnh FOR

Cấu trúc FOR cho phép lặp lại nhiều lần một dãy lệnh. Số lần lặp lại dãy lệnh đã biết trước. Phát biểu FOR có 2 dạng:

FOR .. TO .. DO đếm lên

FOR .. DOWNTO ..DO đếm xuống

* Cú pháp tổng quát là:

FOR <biến đếm> := <trị đầu> TO/DOWNTO <trị cuối> DO <Công việc>;

* Lưu đồ:

Hình 6. 6: Lưu đồ phát biểu FOR .. TO .. DO

Chú ý: Trị đầu, trị cuối là các biến hoặc hằng và biến đếm phải là kiểu rời rạc.

Ví dụ 6.15: Chương trình in một dãy số từ 0 đến 9

PROGRAM Day_So ;

VAR

i : Integer ;

BEGIN

FOR i := 0 TO 9 DO Write (i) ;

Readln ;

END.

b. Lệnh WHILE .. DO

* Lưu đồ của lệnh

Hình 6. 7: Lưu đồ cấu trúc WHILE .. DO

* Ý nghĩa lưu đồ:

Trong khi mà điều kiện còn đúng thì cứ thực hiện Công việc, rồi quay trở về kiểm tra điều kiện lại. Vòng lặp được tiếp tục, đến khi điều kiện đặt ra không còn đúng nữa thì đi tới thực hiện lệnh tiếp theo

* Cú pháp

WHILE <điều kiện> DO <Công việc>

Hình 6.8: Sơ đồ cú pháp lệnh WHILE .. DO

Ghi chú:

• Ðiều kiện trong cấu trúc lặp WHILE .. DO là một biểu thức logic kiểu Boolean chỉ có 2 giá trị là Ðúng (True) hoặc Sai (False)

• Nếu điều kiện Ðúng thì chương trình sẽ chạy trong cấu trúc WHILE .. DO.

• Sau mỗi lần lặp, chương trình trở lại kiểm tra điều kiện. Tùy theo biểu thức logic của điều kiện là Ðúng hay Sai thì chương trình sẽ thực hiện Công việc tương ứng.

• Nếu Sai thì chuyển xuống dưới cấu trúc WHILE .. DO

Ví dụ 6.16: Chương trình tính trung bình n số: x1 + x2 + x3 + ... + xn

Program Trung_binh_Day_So ;

VAR

n, count : Integer ;

x, sum, average : real ;

BEGIN

count := 1 ;

sum := 0 ;

Write (' Nhập n = ') ;

readln (n) ;

WHILE count < n+1 DO

BEGIN

Write (' Nhập giá trị thứ' , count,' của x = ' ) ;

readln (x) ;

sum := sum + x ;

count := count + 1 ;

END ;

average := sum/n ;

Writeln (' Trung bình là =' , average : 10 : 3 ) ;

Writeln (' Nhấn Enter để thoát ...' ) ;

Readln ;

END.

c. Lệnh REPEAT .. UNTIL

Câu lệnh REPEAT .. UNTIL dùng trong các trường hợp khi biến điều khiển không có kiểu rời rạc và đặc biệt trong các trường hợp số lần lặp không biết trước.

Hình 6.9: Lưu đồ cấu trúc của REPEAT .. UNTIL

* Ý nghĩa câu lệnh:

Nếu điều kiện logic là Sai (False) thì lặp lại lệnh cho đến khi điều kiện Ðúng thì mới thoát ra khỏi cấu trúc REPEAT .. UNTIL.

Nếu có nhiều câu lệnh thì mỗi lệnh ngăn cách nhau bằng dấu chấm phẩy (;)Công việc của REPEAT và UNTIL không nhất thiết phải dùng lệnh ghép để nhóm từ 2 lệnh đơn trở lên thành công việc.

Hình 6.10: Sơ đồ cú pháp REPEAT .. UNTIL

Ví dụ 6.17: Với bài toán trung bình cộng một dãy số ở ví dụ trước có thể viết theo cấu trúc REPEAT .. UNTIL như sau:

PROGRAM Trung_binh_Day_So ;

VAR n, count : Integer ;

x, sum : real ;

BEGIN

count := 1 ;

sum := 0 ;

Write := (' Nhập n = ') ; readln (n) ;

REPEAT

Write (' Nhập giá trị thứ' , count, 'của x = ') ;

readln(x) ;

sum := sum + x ;

count := count + 1 ;

UNTIL count > n ;

Writeln (' Trung bình là =' , sum/n : 8 :2 ) ;

Readln ;

END.

Ghi chú:

So sánh 2 cách viết WHILE .. DO và REPEAT .. UNTIL ta thấy có sự khác biệt:

- Trong cấu trúc WHILE .. DO thì <Ðiều kiện> được kiểm tra trước, nếu thỏa <Ðiều kiện> thì mới thực hiện <Công việc>.

- Ngược lại, trong cấu trúc REPEAT .. UNTIL thì <Công việc> sẽ được thực thi trước sau đó mới kiểm tra <Ðiều kiện>, nếu không thỏa <Ðiều kiện> thì tiếp tục thi hành <Công việc> cho đến khi <Ðiều kiện> là đúng.

Lệnh REPEAT .. UNTIL thường được sử dụng trong lập trình, nhất là lúc người sử dụng muốn tiếp tục bài toán ở trường hợp thay đổi biến mà không phải trở về chương trình và nhấn tổ hợp phím Ctrl + F9 lại.

Ví dụ 6.18: Nhân 2 số a và b

PROGRAM Tich;

VAR a, b : integer ;

CK : char ;

BEGIN

REPEAT

Write (' Nhập số a = '); Readln (a) ;

Write (' Nhập số b = '); Readln (b) ;

Writeln (' Tích số của a x b là :' , a*b : 10 ) ;

Writeln (' Tiếp tục tính nữa không (CK) ? ');

Readln (CK) ;

UNTIL upcase(CK) = K; {hàm chuyển đổi ký tự trong biến}

{CK thành ký tự in hoa}

END.

BÀI ÐỌC THÊM TOP

NHẬP VÀ XUẤT DỮ LIỆU TRONG TURBO PASCAL

--- oOo ---

Thông thường, chương trình Turbo Pascal được đặt trong một thư mục riêng rẽ có tên TP. Ðể sử dụng Turbo Pascal, ta cần có các tập tin tối thiểu:

- TURBO.EXE - TURBO.TPL

- TURBO.TP - GRAPH.TPU

- Các file đồ họa : *.BGI - Các Font chữ trong đồ họa : *.CHR

Sử dụng câu lệnh Turbo và nhấn Enter, màn hình sẽ xuất hiện :

Ðể trợ giúp người sử dụng, phím chức năng F10 có tác dụng mở các Menu với nhiều Options khác nhau. Ta cũng có thể kích hoạt trên thanh Menu chính bằng cách kết hợp phím <Alt - Ký tự mục tương ứng>, ví dụ để kích hoạt mục File, ta nhấn đồng thời phím Alt- F, sau đó dùng các phím mũi tên và nút Enter để chọn lựa và ra lệnh thi hành. Phím F1 trợ giúp thể hiện các thông tin trên màn hình.

Ta có thể sử dụng các tổ hợp phím để tạo ra các khối chữ hoặc câu lệnh (trên màn hình thấy có sự thay đổi màu) để ta có thể sao chép, cắt dán, xóa bỏ ...

Ctrl-K-B Ðánh dấu đầu khối

Ctrl-K-K Ðánh dấu cuối khối

Ctrl-K-C Chép khối tại sau vị trí con trỏ

Ctrl-K-V Di chuyển khối tới sau vị trí con trỏ

Ctrl-K-Y Xóa khối hiện hành

Ctrl-K-W Ghi khối hiện hành vào đĩa như một tập tin

Ctrl-K-R Ðọc khối tập tin đã ghi vào đĩa vào sau vị trí con trỏ

Ctrl-K-H Tắt/ Mở khối

Một chương trình máy tính, sẽ có các bước căn bản sau:

Trong thảo chương Turbo Pascal, các thủ tục nhập dữ liệu được dùng:

THỦ TỤC NHẬP Ý NGHĨA

READ(x1, x2, ..., xn) Nhập các biến x1, x2, ..., xn theo hàng ngang

từ bàn phím (con trỏ không xuống hàng).

READLN(x1, x2, ..., xn) Nhập các biến x1, x2, ..., xn theo hàng dọc

từ bàn phím (mỗi lần nhập con trỏ xuống hàng).

READLN; Dừng chương trình, đợi Enter mới tiếp tục.

ASSIGN(F, File_Name); Mở tập tin F có tên là File_Name

RESET(F); Chuẩn bị đọc tập tin

READ(F, x1, x2, ..., xn) ; Ðọc các giá trị trên tập tin F ra các biến x1, x2, ..., xn

tương ứng

CH := ReadKey ; Ðọc một ký tự từ bàn phím vào biến ký tự CH

KEYPRESSED Một hàm có giá trị là TRUE nếu có một phím

được bấm và là FALSE nếu ngược lại.

THỦ TỤC XUẤT Ý NGHĨA

WRITE(x1, x2, ..., xn) Viết giá trị trong các biến x1, x2, ..., xn ra màn hình

theo hàng ngang (con trỏ không xuống hàng).

WRITELN(x1, x2, ..., xn) Viết giá trị trong các biến x1, x2, ..., xn ra màn hình

theo hàng dọc (mỗi lần viết trị x có xuống hàng).

WRITELN; Xuống hàng

WRITELN(I : n); Viết ra giá trị của biến nguyên I vào n chỗ tính từ

phải sang trái. Nếu dư chỗ (chữ số của I < n) sẽ để trống

WRITELN(R : n : m); Viết ra giá trị của biến thực R vào n chỗ, chỉ lấy

m số thập phân.

WRITELN( abc... ); Viết ra nguyên văn chuỗi ký tự abc...

WRITELN (LST, x1, x2, ..., xn) Viết ra máy in các trị biến x1, x2, ..., xn

ASSIGN(F, File_Name) Mở tập tin F có tên là File_Name

REWRITE(F) ; để chuẩn bị viết vào

WRITE (F, x1, x2, ..., xn) ; Viết các giá trị x1, x2, ..., xn vào tập tin F

CLOSE (F) ; Ðóng tập tin F

Cần lưu trữ chương trình ta dùng phím F2.

Mở một file đã có ta dùng phím F3.

Ðể thay đổi kích thước/Di chuyển cửa sổ chương trình, dùng phím F5 và

Ctrl+F5.

Trường hợp mở nhiều chương trình, ta dùng phím F6 và Ctrl+F6 để đi đến/trở

về trước chương trình hiện hành.

Ðể biên dịch và kiểm tra lỗi, ta dùng phím F9.

Ðể chạy chương trình đã soạn thảo xong, đánh Ctrl+F9

Muốn thoát khỏi Turbo Pascal và trở về DOS, đánh Alt+X.

CHƯƠNG 7

CHƯƠNG TRÌNH CON VÀ ÐƠN VỊ CHƯƠNG TRÌNH

I. KHÁI NIỆM VỀ CHƯƠNG TRÌNH CON.

II. THỦ TỤC VÀ HÀM.

1. Thủ tục.

2. Hàm.

III. TRUYỀN THAM SỐ CHO CHƯƠNG TRÌNH CON.

IV. TÍNH ÐỆ QUI TRONG CHƯƠNG TRÌNH CON.

V. UNIT.

1. Khái niệm.

2. Các Unit chuẩn.

VI. UNIT TỰ TẠO.

1. Các bước để tạo ra một Unit.

2. Ví dụ.

BÀI ÐỌC THÊM LẬP TRÌNH THEO CẤU TRÚC TRÊN - XUỐNG.

I. KHÁI NIỆM VỀ CHƯƠNG TRÌNH CON TOP

Khi lập trình, chúng ta thường có những đoạn chương trình hay phép tính lặp lại nhiều lần. Nếu mỗi lần lặp lại, ta phải viết những đoạn lệnh như nhau thì chương trình của chúng ta trở nên dài dòng, rối rắm và mất thời gian vô ích. Ðể giải quyết những trường hợp như vậy, Pascal cho phép chúng ta tạo ra các module, mỗi module mang một đoạn chương trình gọi là chương trình con (subroutine hay subprogram). Mỗi chương trình con sẽ mang một cái tên khác nhau. Một module chỉ cần viết một lần và sau đó chúng ta có thể truy xuất nó nhiều lần, bất kỳ nơi nào trong chương trình chính. Khi cần thiết, chúng ta chỉ việc gọi tên chương trình con đó ra để thi hành lệnh.

Nhờ sử dụng chương trình con, chương trình có thể tiết kiệm được ô nhớ. Ðồng thời, ta có thể kiểm tra tính logic trong tiến trình lập trình cho máy tính điện tử, có thể nhanh chóng loại bỏ những sai sót khi cần hiệu chỉnh hay cải tiến chương trình. Ðây là khái niệm cơ bản trong ý tưởng lập chương trình có cấu trúc. Một quá trình tính cũng có thể có nhiều chương trình con lồng ghép vào nhau.

Trong Pascal, chương trình con được viết dưới dạng thủ tục (procedure) và hàm (function). Cấu trúc của 2 kiểu chương trình con này thì tương tự với nhau, mặc dầu cách truy xuất của chúng có khác nhau và cách trao đổi thông tin trong mỗi kiểu cũng có điểm khác nhau. Hàm (function) trả lại một giá trị kết quả vô hướng thông qua tên hàm và hàm được sử dụng trong biểu thức.

Ví dụ hàm chuẩn, như hàm sin(x) mà chúng ta đã biết trong chương trước có thể được xem như một chương trình con kiểu function với tên là sin và tham số là x. Trong khi đó, thủ tục (procedure) không trả lại kết quả thông qua tên của nó, do vậy, ta không thể viết các thủ tục trong biểu thức. Các lệnh Writeln, Readln trong chương trước được xem như các thủ tục chuẩn.

Một chương trình có chương trình con tự thiết lập có 3 khối (block) :

* Khối khai báo

* Khối chương trình con

* Khối chương trình chính

II . THỦ TỤC VÀ HÀM TOP

* Một số khái niệm biến:

• Biến toàn cục (global variable): Còn được gọi là biến chung, là biến được khai báo ở đầu chương trình, nó được sử dụng bên trong chương trình chính và cả bên trong chương trình con. Biến toàn cục sẽ tồn tại trong suốt quá trình thực hiện chương trình.

• Biến cục bộ (local variable): Còn được gọi là biến riêng, là biến được khai báo ở đầu chương trình con, và nó chỉ được sử dụng bên trong thân chương trình con hoặc bên trong thân chương trình con khác nằm bên trong nó (các chương trình con lồng nhau). Biến cục bộ chỉ tồn tại khi chương trình con đang hoạt động, nghĩa là biến cục bộ sẽ được cấp phát bộ nhớ khi chương trình con được gọi để thi hành, và nó sẽ được giải phóng ngay sau khi chương trình con kết thúc.

• Tham số thực (actual parameter) là một tham số mà nó có thể là một biến toàn cục, một biểu thức hoặc một giá trị số (cũng có thể biến cục bộ khi sử dụng chương trình con lồng nhau) mà ta dùng chúng khi truyền giá trị cho các tham số hình thức tương ứng của chương trình con.

• Tham số hình thức (formal parameter) là các biến được khai báo ngay sau Tên chương trình con, nó dùng để nhận giá trị của các tham số thực truyền đến. Tham số hình thức cũng là một biến cục bộ, ta có thể xem nó như là các đối số của hàm toán học.

* Lời gọi chương trình con (thủ tục và hàm):

Ðể chương rrình con được thi hành, ta phải có lời gọi đến chương trình con, lời gọi chương trình con thông qua tên chương trình con và danh sách các tham số tương ứng (nếu có). Các qui tắc của lời gọi chương trình con:

• Trong thân chương trình chính hoặc thân chương trình con, ta chỉ có thể gọi tới các chương trình con trực thuộc nó.

• Trong chương trình con, ta có thể gọi các chương trình con ngang cấp đã được thiết lập trước đó.

1. Thủ tục (Procedure): TOP

Thủ tục là một đoạn cấu trúc chương trình được chứa bên trong chương trình Pascal như là một chương trình con. Thủ tục được đặt tên và có thể chứa danh sách tham số hình thức (formal parameters). Các tham số này phải được đặt trong dấu ngoặc đơn ( ). Ta có thể truy xuất thủ tục bằng cách gọi tên của thủ tục. Chương trình sẽ tự động truy xuất thủ tục đúng tên đã gọi và thực hiện các lệnh chứa trong thủ tục đó. Sau khi thực hiện thủ tục xong, chương trình sẽ trở lại ngay lập tức sau vị trí câu lệnh gọi thủ tục đó.

Có 2 loại thủ tục:

+ thủ tục không tham số

+ và thủ tục có tham số.

a. Cấu trúc của thủ tục không tham số

PROCEDURE < Tên thủ tục > ;

{ Các khai báo hằng, biến, kiểu cục bộ... }

BEGIN

{ ... các lệnh trong nội bộ thủ tục ... }

END ;

Ví dụ 7.1: Tìm số lớn nhất trong 3 trị số nguyên

PROGRAM Largest ; (* Xác định số lớn nhất trong 3 trị số nguyên được nhập vào *)

VAR a, b, c : integer ;

yn : char ;

PROCEDURE maximum ;

VAR max : integer ;

BEGIN

IF a > b THEN max := a ELSE max := b ;

IF c > max THEN max := c ;

Writeln (' Số lớn nhất là' , max ) ;

END ;

BEGIN (* Ðoạn chương trình chính *)

yn := 'Y' ;

WHILE ( upcase(yn) = 'Y ') DO

BEGIN

Writeln (' Nhập 3 số nguyên : ') ;

Readln (a, b, c ) ;

maximum ; (* --- Lời gọi thủ tục maximum --- *)

Write (' Tiếp tục nhập 3 số mới không (y/n) ? ') ;

Readln (yn) ;

END ;

END.

Chú ý:

Trong chương trình trên, thủ tục maximum được khai báo trước khi nó được truy xuất, các biến a, b, c được gọi nhập vào ở chương trình chính và biến max được định nghĩa bên trong thủ tục. Ðiều này cho ta thấy, không phải lúc nào cũng cần thiết khai báo biến ngay đầu chương trình chính.

b. Cấu trúc của thủ tục có tham số

PROCEDURE < Tên thủ tục > (<danh sách tham số hình thức : kiểu biến>);

{ Các khai báo hằng, biến, kiểu cục bộ ... }

BEGIN

{ ... các lệnh trong nội bộ thủ tục ... }

END ;

Khi viết một thủ tục, nếu có các tham số cần thiết, ta phải khai báo nó (kiểu, số lượng, tính chất, ...). Các tham số này gọi là tham số hình thức (formal parameters).

Một thủ tục có thể có 1 hoặc nhiều tham số hình thức. Khi các tham số hình thức có cùng một kiểu thì ta viết chúng cách nhau bởi dấu phẩy (,). Trường hợp các kiểu của chúng khác nhau hoặc giữa khai báo tham số truyền bằng tham biến và truyền bằng tham trị (sẽ học ở phần sau ) thì ta phải viết cách nhau bằng dấu chấm phẩy (;).

Ví dụ 7.2: Tính giai thừa của một số

PROGRAM Tinh_Giai_thua ;

VAR

n : integer ; gt : real ; {các biến chung}

PROCEDURE giaithua (m : integer );

VAR i : integer ; {i là biến riêng}

BEGIN

gt := 1 ;

FOR i := 1 TO m DO gt := gt * i ;

END ;

BEGIN (* Thân chương trình chính *)

Write('Nhập số nguyên n (0 <= n < 33) = ') ; Readln (n) ;

If n>=0 then

Begin

giaithua (n) ;

Writeln ('Giai thừa của , n, là :' , gt: 10 : 0) ;

End

Else Writeln(' Không tính giai thừa của một số âm! ') ;

Readln;

END.

Trong chương trình trên m là các tham số hình thức của thủ tục giaithua.

Khi gọi thủ tục giaithua (n) thì tham số thực n được truyền tương ứng cho tham số hình thức m.

Ví dụ 7.3: Giải phương trình ax2 + bx + c = 0, theo dạng chương trình con lồng nhau:

PROGRAM Giai_PTB2;

VAR hsa, hsb, hsc:real; {các biến toàn cục}

PROCEDURE Ptb2(a,b,c:real); {a, b, c là các tham số hình thức của Ptb2}

Var delta:real; {biến cục bộ}

PROCEDURE Ptb1(a1,b1:real); {a,b là các tham số hình thức của Ptb1}

Begin

if a1=0 then

if b1=0 then

writeln('Phương trình vô số nghiệm')

else

writeln('Phương trình vô nghiệm')

else

writeln('Phương trình có nghiệm =',-b1/a1:8:2);

End; {kết thúc thủ tục Ptb1}

Begin {bắt đầu thủ tục Ptb2}

(3) if a=0 then ptb1(b,c) {b, c là các tham số thực cho Ptb1}

(4) else

delta:=sqr(b)-4*a*c;

if delta>0 then

writeln('Nghiệm x1= ',(-b+sqrt(delta))/(2*a):8:2);

writeln('Nghiệm x2= ',(-b-sqrt(delta))/(2*a):8:2);

end

else

if delta=0 then

writeln('Nghiệm kép x1=x2= ',-b/(2*a):8:2)

else

writeln('delta <0 => Phương trình vô nghiệm');

end;

End; {kết thúc thủ tục Ptb2}

Begin {chương trình chính}

(1) write('Nhập các hệ số a, b, c = ');readln(hsa, hsb, hsc);

(2) Ptb2(hsa,hsb,hsc); {hsa, hsb, hsc là các tham số thực cho Ptb2}

(5) readln;

End. {kết thúc chương trình}

Ở ví dụ trên, thì thủ tục Ptb2 và thủ tục Ptb1 được gọi là thủ tục lồng nhau.

Ở dòng (4), ta thấy hsa, hsb, hsc lại được hiểu là các tham số thực, chúng truyền giá trị biến cho các tham số hình thức a, b, c tương ứng trong thủ tục Ptb2.

Nếu ta lại xét đến thủ tục con của thủ tục Ptb2 là Ptb1 thì các tham số a, b, c này (chính xác là b và c) lại là tham số thực đối với Ptb1, với b và c được truyền tương ứng cho các tham số hình thức a, b của thủ tục Ptb1.

Như vậy ta nhận thấy rằng, vấn đề xác định được đâu là biến toàn cục, đâu là biến cục bộ, đâu là tham số thực và đâu là tham số hình thức (tham số biến và tham số trị) là ứng bước nào mà chương trình đang thực hiện? Ðây là phần then chốt để nắm được cách vận hành và kết quả của chương trình xử lý.

Sơ đồ minh họa cách vận hành và quản lý biến của chương trình:

2. Hàm (Function) : TOP

Hàm là một chương trình con cho ta 1 giá trị kiểu vô hướng. Hàm tương tự như thủ tục nhưng trả về một giá trị thông qua tên hàm và lời gọi hàm tham gia trong biểu thức.

Cấu trúc một hàm tự đặt gồm:

FUNCTION <Tên hàm> (<Tham số hình thức : kiểu biến>) : <Kiểu kết quả> ;

{ các khai báo hằng, biến, kiểu cụcbbộ... }

BEGIN

{ ... các khai báo trong nội bộ hàm ... }

END ;

Trong đó:

- Tên hàm là tên tự đặt cần tuân thủ theo nguyên tắc đặt tên trong Pascal.

- Kiểu kết quả là một kiểu vô hướng, biểu diễn kết quả giá trị của hàm.

- Một hàm có thể có 1 hay nhiều tham số hình thức, khi có nhiều tham số hình thức cùng một kiểu giá trị thì ta có thể viết chúng cách nhau bằng dấu phẩy (,). Trường hợp các tham số hình thức khác kiểu thì ta viết chúng cách nhau bằng dấu chấm phẩy (;).

- Trong hàm có thể sử dụng các hằng, kiểu, biến đã được khai báo trong chương trình chính nhưng ta có thể khai báo thêm các hằng, kiểu, biến dùng riêng trong nội bộ hàm. Chú ý là phải có một biến trung gian có cùng kiểu kết quả của hàm để lưu kết quả của hàm trong quá trình tính toán để cuối cùng ta có 1 lệnh gán giá trị của biến trung gian cho tên hàm.

Ví dụ 7.4: FUNCTION TINH (x, y : integer ; z : real ) : real ;

Ðây là một hàm số có tên là TINH với 3 tham số hình thức x, y, z. Kiểu của x và y là kiểu số nguyên integer còn kiểu của z là kiểu số thực real. Hàm TINH sẽ cho kết quả kiểu số thực real.

Ví dụ 7.5: Bài toán tính giai thừa (factorials)

PROGRAM giaithua ;

VAR x : integer ;

FUNCTION factorial (n : integer) : integer ;

VAR heso, tichso : integer ;

BEGIN

tichso := 1 ;

IF n <= 1 THEN factorial := 1

ELSE BEGIN

FOR heso := 2 TO n DO

tichso := tichso * heso ;

factorial := tichso;

END ;

END ;

BEGIN

Write (' Nhập vào một số nguyên dương x = '); Readln (x) ;

Writeln (' Với x = , x , thì giai thừa sẽ là : x ! = ' , factorial(x))

Readln;

END.

Ghi chú :

Khi khai báo kiểu dữ kiệu cho các tham số hình thức trong thủ tục và hàm, ta cần phải chú ý điểm sau:

Nếu kiểu dữ liệu của các tham số hình thức là các kiểu dữ liệu có cấu trúc (kiểu array, string, kiểu record,... ) thì việc khai báo kiểu dữ liệu cho các tham số hình thức nên được khai báo theo cách gián tiếp, tức là phải thông qua từ khóa TYPE.

Ví dụ 7.6: Procedure Xuat1(hoten : string[25]);

Procedure Xuat2(mang: array[1..10] of integer);

Hai chương trình con Xuat1 và Xuat2 đều bị lỗi ở phần khai báo kiểu dữ liệu cho hai tham số hình thức là hoten và mang.

Ðể khắc phục lỗi này, ta sẽ khai báo gián tiếp một kiểu dữ liệu str25 và M10 thông qua từ khóa TYPE như sau:

TYPE

Str25=string[25]; {Str25 là một kiểu chuỗi có độ dài 25}

M10=Array[1..10] of integer; {M10 là một kiểu dữ kiệu mảng có 10 phần tử nguyên}

Tiếp đến, dùng 2 kiểu dữ liệu mới định nghĩa Str25 và M10 để định kiểu cho các tham số hình thức hoten và mang như sau:

Procedure Xuat1(hoten : Str25);

Procedure Xuat2(mang: M10);

III. TRUYỀN THAM SỐ CHO CHƯƠNG TRÌNH CON TOP

Khi truyền tham số trong Pascal, đòi hỏi phải có sự tương ứng về tên của kiểu dữ liệu của các tham số hình thức và tham số thực. Một số định nghĩa và qui tắc về truyền tham số trong Pascal:

- Những tham số hình thức nằm sau từ khóa VAR gọi là tham số biến (variable parameter). Với tham số biến, các tham số thực bắt buộc phải là biến chứ không được là giá trị. Khi giá trị của tham số biến thay đổi thì nó sẽ làm thay đổi giá trị của tham số thực tương ứng và khi ra khỏi chương trình con đó, tham số thực vẫn giữ giá trị đã được thay đổi đó.

- Những tham số hình thức không đứng sau từ khóa VAR gọi là tham số trị (value parameter), khi đó các tham số thực có thể là một biến, một biểu thức, một hằng, hoặc một giá trị số. Các tham số trị nhận giá trị từ tham số thực khi truyền như là giá trị ban đầu, khi giá trị của tham số trị thay đổi thì nó sẽ không làm thay đổi giá trị của tham số thực, nghĩa là giá trị của tham số thực sau khi thoát khỏi chương trình con vẫn luôn bằng với giá trị của tham số thực trước khi truyền đến chương trình con đó. Do vậy một tham trị không bao giờ là kết quả tính toán của chương trình con.

Một vài thí dụ về tham số biến:

Ví dụ 7.7: Viết chương trình tính lập phương.

PROGRAM Parameter1;

VAR num: integer; {num là biến toàn cục}

PROCEDURE LapPhuong(var a:integer); {a là một tham số biến}

Begin

a:=a*a*a;

End;

Begin

write('Nhập số cần tính lập phương num = ');

readln(num);

LapPhuong(num); {tham số thực num được truyền cho tham số biến a}

writeln('Lập phương của số vừa nhập =' , num);

readln;

End.

Ví dụ 7.8:

PROGRAM parameter2;

VAR a, b : integer ; {biến toàn cục }

PROCEDURE thamso (x : integer ; VAR y : integer ) ;

BEGIN { x: là tham số trị , còn y là tham số biến}

x := x + 1 ;

y := y + 1 ;

Writeln ('Trong procedure thamso, ... ') ;

Writeln (' Hai số của bạn là a = , x : 3, và b = , y : 3 ) ;

END ;

BEGIN

Write (' Nhập vào 2 trị số nguyên a, b : ') ; Readln (a, b) ;

Writeln (' Ban đầu, Bạn đã nhập vào a =' , a : 3, 'và b =' , b : 3 ) ;

thamso (a, b) ; {tham số thực a truyền cho tham số trị x

tham số thực b truyền cho tham số biến y }

Writeln (' Ngoài procedure thamso, ... ');

Writeln (' Hiện nay, số a là , a : 3, và b là , b : 3 ') ;

Writeln (' Ta thấy, a không đổi và b thay đổi ! ') ;

Readln;

END.

IV. TÍNH ÐỆ QUI CỦA CHƯƠNG TRÌNH CON TOP

Một chương trình con mà trong quá trình thiết lập, nó sẽ gọi chính bản thân nó thì chương trình con có tính đệ qui (recursion).

Ví dụ 7.9: Bài toán tính giai thừa (factorials) theo cách đệ qui. Bài toán này có phần chương trình chính giống như đã có ở ví dụ trước:

PROGRAM Giaithua ; (*Tính giai thừa của số n theo phương pháp đệ qui *)

VAR x : integer ;

FUNCTION factorial (n : integer) : longint ;

BEGIN

IF n <= 1 THEN factorial := 1 {điều kiện neo}

ELSE factorial := n * factorial (n -1);

END ;

BEGIN

Write (' Nhập vào một số nguyên dương x = ');

Readln (x) ;

Writeln ;

Writeln (' Kết quả ',x,'! = , factorial(x));

Readln;

END.

Giả sử ta nhập x = 4, thì 4! = factorial(n), với n = 4, ta có sơ đồ minh họa như sau:

Chú ý:

- Ưu điểm của thuật toán đệ qui là ngắn gọn. Nó có khả năng định nghĩa một tập hợp rất lớn các đối tượng bằng một số các câu lệnh hữu hạn. Thuật toán đệ qui có vẻ thích hợp cho các bài toán mà tự thân cấu trúc dữ liệu của nó đã được định nghĩa theo lối đệ qui.

- Có một số thuật toán đệ qui sử dụng cho các bài toán đơn giản có thể được thay thế bằng một thuật toán khác không tự gọi chúng, sự thay thế đó được gọi là khử đệ qui.

- Trong một số bài toán ta có thể giải theo 2 cách: thuật toán lặp (xem chương trước) và thuật toán đệ qui. Thông thường, cách giải theo thuật toán lặp (WHILE .. DO) thì tốt hơn so với thuật toán đệ qui vì đệ qui đòi hỏi thêm bộ nhớ và thời gian. Khi đó các thanh ghi được sử dụng cho lưu trữ và khi quay trở về phải khôi phục lại trạng thái cũ trong mỗi lần gọi đến chương trình con. Mức độ phức tạp có thể gia tăng khi trong chương trình con theo thuật toán đệ qui có chứa những chương trình con khác. Vì vậy, khi dùng đệ qui ta cần thận trọng, nhất là thuật toán này thường không cho ta thấy rõ trực tiếp toàn bộ quá trình giải các bước. Nói chung, chỉ khi naò không thể dùng thuật toán lặp ta mới nên sử dụng thuật toán đệ qui.

V. ÐƠN VỊ CHƯƠNG TRÌNH (UNIT)

1. Khái niệm TOP

Lập trình một bài toán lớn rất phức tạp và vất vả nếu nó phải sử dụng nhiều thuật toán lập đi lập lại. Việc tạo ra nhiều mẫu chương trình con nhằm giảm nhẹ công việc của một lập trình viên (programmer). Tuy nhiên, mỗi chương trình con chỉ có ứng dụng được trong chính chương trình chứa nó mà thôi. Ðể khỏi mất thời gian để viết lại chúng, người ta biến mỗi chương trình con thành các module độc lập, được biên dịch sẵn và lưu trữ trên đĩa như một thư viện. Khi cần ta cứ việc gọi các module này ra mà không cần phải viết lại chúng. Mỗi module như vậy được gọi là một đơn vị chương trình, hay gọi tắt là UNIT.

Khái niệm Unit đã được vào sử dụng từ chương trình Pascal version 4.0 trở đi. Có hai loại Unit là các Unit chuẩn do Turbo Pascal tạo sẵn và các Unit tự tạo do người lập trình tự viết ra.

2. Các Unit chuẩn TOP

a. Một số Unit chuẩn trong Turbo Pascal 5.5 trở đi

* Unit SYSTEM : gồm các hằng, biến, kiểu, hàm, thủ tục trong version 3.0

* Unit CRT : gồm các hằng, biến, kiểu, hàm, thủ tục liên quan đến

chế độ Text của version 5.5

* Unit PRINTER : gồm các hằng, biến, kiểu, hàm, thủ tục liên quan đến

chế độ in ấn.

* Unit GRAPH : gồm các hằng, biến, kiểu, hàm, thủ tục liên quan đến

chế độ đồ thị của version 5.5

* Unit TURBO3 : gồm các hằng, biến, kiểu, hàm, thủ tục liên quan đến

chế độ Text của version 3.0

* Unit GRAPH3 : gồm các hằng, biến, kiểu, hàm, thủ tục liên quan đến

chế độ đồ thị của version 3.0

* Unit DOS : gồm các hằng, biến, kiểu, hàm, thủ tục liên quan đến hệ điều hành MS-DOS

* Unit OVERLAY : gồm các hằng, biến, kiểu, hàm, thủ tục liên quan đến

việc truy xuất đĩa phủ lấp khi chạy chương trình.

Các Unit trên được lưu trữ trong tập tin TURBO.TPL của Turbo Pascal.

Chúng ta có thể sử dụng chương trình TPUMOVER.EXE để lấy ra hoặc đưa vào một hay nhiều Unit nhằm tiết kiệm bộ nhớ hay tăng cường tiện ích sử dụng.

b. Cách gọi Unit

Muốn sử dụng UNIT thì trong đầu chương trình ta phải khai báo Unit theo cú pháp sau:

USES <Tên Unit> [ { , <Tên Unit> } ] ;

Ví dụ 7.10 USES CRT, GRAPH ;

Ðặc biệt, chỉ riêng Unit SYSTEM thì không cần phải khai báo.

c. Một số thủ tục và hàm trong Unit CRT

* ClrScr : thủ tục xóa màn hình

* ClrEol : thủ tục xóa ký tự bên phải con trỏ màn hình, sau khi xóa con trỏ vẫn ở tại chỗ

* InsLine : thủ tục xen vào một hàng ở vị trí con trỏ màn hình

* DelLine : thủ tục xóa bỏ một hàng ở vị trí con trỏ màn hình

* GotoXY(XPos, Ypos): đưa con trỏ màn hình về vị trí có tọa độ Xpos và

Ypos. X có giá trị từ 1 - 80, và Y có giá trị từ 1 - 25

* Delay(time): tạo thời gian trễ tính theo milisecond. Time là một số nguyên

dùng để làm chậm chương trình cho ta kịp quan sát dữ liệu

* Sound(F) : thủ tục tạo ra âm thanh với tần số F (hz). F là số nguyên

* NoSound : thủ tục tắt âm thanh

* LowVideo và NormVideo: thủ tục màn hình, khi gọi LowVideo thì mọi ký

tự viết ra màn hình có độ sáng yếu dần đi cho đến

khi nhận thủ tục NormVideo mới về độ sáng bình thường.

* TextBackGround (color): thủ tục chọn màu nền, color cho ở bảng (từ 1 -7)

* KeyPressed: hàm cho giá trị kiểu kết quả Boolean, cho giá trị là True nếu có một phím được bấm.

* TextColor (color): thủ tục chọn màu chữ, color lấy ở bảng

Các hằng số màu của CRT unit

Hằng số color Màu hiển thị Giá trị

Black

Blue

Green

Cyan

Red

Magenta

Brown

LightGray

DarkGray

LightBlue

LightGreen

LightCyan

LightRed

LightMagenta

Yellow

White Ðen

Xanh da trời

Xanh lá cây

Xanh lơ

Ðỏ

Tím

Nâu

Xám nhạt

Xám đậm

Xanh da trời nhạt

Xanh là cây nhạt

Xanh lơ nhạt

Ðỏ nhạt

Tím nhạt

Vàng

Trắng 0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

Ðể tạo ra màu nhấp nháy, ta cộng thêm hằng số Blink của CRT vào bất kỳ màu chữ mà ta chọn. Ta không thể làm nhấp nháy màu nền.

Ví dụ để trình bày chữ vàng trên nền xanh, ta viết:

TextColor(Yellow) ; TextBackground(Blue) ;

Muốn có một dòng chữ đỏ nhấp nháy thì ra lệnh:

TextColor(Red + Blink) ;

* ReadKey : hàm có kiểu kết quả là Char, khi nhấn các phím chức năng trên bàn phím thì có kết quả là 1 ký tự mã ASCII. Nếu ký tự đầu tiên do ReadKey trả về bằng ASCII 0 thì ký tự kế theo sẽ chỉ định phím như ở bảng dưới. Các phím Home, phím mũi tên, ... luôn tạo nên một ký tự đi theo ASCII 0. Các phím chức năng từ F1 đến F10 sinh ra một trong 4 ký tự tùy theo ta dùng với tổ hợp phím Alt, Ctrl hay Shift hay dùng một mình.

Các phím chức năng đặc biệt

Tên phím Bình thường Alt Ctrl Shift

F1

F2

F3

F4

F5

F6

F7

F8

F9

F10

Home

PageUp

. ¬

® .

End

¯ .

PageDn

Ins

Del : (59)

< (60)

= (61)

> (62)

? (63)

@ (64)

A (65)

B (66)

C (67)

D (68)

G (71)

H (72)

I (73)

K (75)

M (77)

O (79)

P (80)

Q (81)

R (82)

S (83) h (104)

i (105)

j (106)

k (107)

l (108)

m (109)

n (110)

o (111)

p (112)

q (113) ^ (94)

- (95)

. (96)

a (97)

b (98)

c (99)

d (100)

e (101)

f (102)

g (103) T (84)

U (85)

V (86)

W (87)

X (88)

Y (89)

Z (90)

[ (91)

\ (92)

] (93)

VI. UNIT TỰ TẠO

1. Một số bước để tạo ra Unit TOP

Ðể tạo ra một Unit của mình cần đi qua các bước sau:

Bước 1 : Tạo ra một file Unit có phần mở rộng là .PAS với bố cục sau:

UNIT <Tên Unit> ; (* Chú ý : Tên Unit phải trùng với tên File *)

INTERFACE (* Chú ý : Phần giao diện với bên ngoài, không có dấu ; ở đây *)

[Uses <danh sách các unit>]; {Khai báo các unit dùng trong chương trình }

[Khai báo các hằng, kiểu, biến dùng chung] ;

[Khai báo các thủ tục, hàm (tên, danh sách tham số của thủ tục và hàm] ;

IMPLEMENTATION (* Cài đặt các hàm, thủ tục của Unit, không có dấu ; ở đây *)

[Các khai báo kiểu, hằng, biến cục bộ ];

[Nội dung cài đặt các thủ tục, hàm của unit ];

[BEGIN] (* Phần khởi tạo : Initialization Part *)

[Các lệnh khởi tạo ];

END. (* Dù có BEGIN để khởi tạo hay không, ở đây vẫn có END. *)

Bước 2 : Dịch file này lên đĩa theo trình tự sau:

i). Gõ Alt - C để vào Menu COMPILE

ii). Ði đến mục Destination và nhấn Enter để chương trình tự động đổi Memory thành Disk

iii). Gõ C (hoặc F9) để biên dịch chương trình tạo nên một file .TPU

iv). Khi dịch xong gõ một phím bất kỳ. Sau đó ta có thể lập lại bước a và b

để chuyển Destination từ Disk sang Memory.

2. Ví dụ TOP

Ví dụ 7.11: Tạo một UNIT tính Cộng, Trừ, Nhân, Chia cho học sinh tiểu học.

Tên file Unit là TTIEUHOC.PAS với nội dung sau:

UNIT TTieuHoc ; {Phần đầu : Chương trình Toán Tiểu học }

INTERFACE {Phần giao diện}

PROCEDURE Cong (Var So1, So2, So3 : Real) ;

PROCEDURE Tru (Var So1, So2, So3 : Real) ;

PROCEDURE Nhan (Var So1, So2, So3 : Real) ;

PROCEDURE Chia (Var So1, So2, So3 : Real) ;

INPLEMENTATION {Phần cài đặt }

PROCEDURE Cong ;

BEGIN

IF So1 + So2 = So3 THEN Writeln ('Giỏi lắm ! Em đã làm đúng! ')

ELSE Writeln (' Rất tiếc, em đã làm sai ! ');

END;

PROCEDURE Tru ;

BEGIN

IF So1 - So2 = So3 THEN Writeln (' Giỏi lắm ! Em đã làm đúng!')

ELSE Writeln (' Rất tiếc, em đã làm sai ! ');

END;

PROCEDURE Nhan ;

BEGIN

IF So1 * So2 = So3 THEN Writeln ('Giỏi lắm ! Em đã làm đúng !')

ELSE Writeln (' Rất tiếc, em đã làm sai ! ');

END;

PROCEDURE Chia ;

BEGIN

IF So2 = 0 THEN Writeln ('Số chia phải khác 0')

ELSE

IF So1 / So2 = So3 THEN Writeln ('Giỏi lắm! Em đã làm đúng! ')

ELSE Writeln (' Rất tiếc, em đã làm sai ! ');

END;

END. {Chấm dứt UNIT }

Sau khi gõ chương trình Unit trên, đổi Compile Destination thành Disk, biên dịch và tạo tập tin TTIEUHOC.TPU trên đĩa.

Chương trình Pascal cho bài toán Cộng, trừ, Nhân, Chia dùng Unit TTIEUHOC:

PROGRAM Toan_Tieu_Hoc ;

USES CRT, TTieuHoc ;

VAR

chon : Integer ;

So1, So2, So3 : Real ;

PROCEDURE Menu (Var chon : integer) ;

BEGIN

ClrScr ;

Writeln (' == TOÁN TIỂU HỌC == ') ;

Writeln (' = 0. Chấm dứt = ') ;

Writeln (' = 1. Toán cộng = ') ;

Writeln (' = 2. Toán trừ = ') ;

Writeln (' = 3. Toán nhân = ') ;

Writeln (' = 4. Toán chia = ') ;

Writeln (' ================== ') ;

Write (' Bạn chọn số mấy ? ') ;

Readln (chon);

END ;

PROCEDURE nhapso (Var So1, So2, So3 : real );

BEGIN

Write (' Nhập vào số thứ 1 : ') ; Readln(So1) ;

Write (' Nhập vào số thứ 2 : ') ; Readln(So2) ;

Write (' Kết quả là : ') ; Readln (So3) ;

END ;

{=====================Chương Trình Chính ========================}

BEGIN

CLRSCR;

REPEAT

Menu (chon) ;

CASE chon OF

1 : BEGIN

Writeln ;

Writeln (' == Toán cộng == ') ;

Nhapso(So1, So2, So3) ;

Cong(So1, So2, So3) ;

END;

2 : BEGIN

Writeln ;

Writeln (' == Toán trừ == ') ;

Nhapso(So1, So2, So3) ;

Tru(So1, So2, So3) ;

END;

3 : BEGIN

Writeln ;

Writeln (' == Toán nhân == ') ;

Nhapso(So1, So2, So3) ;

Nhan(So1, So2, So3) ;

END;

4 : BEGIN

Writeln ;

Writeln (' == Toán chia == ') ;

Nhapso(So1, So2, So3) ;

Chia(So1, So2, So3) ;

END;

END; {case}

Writeln (' Gõ bất kỳ phím nào để tiếp tục ... ');

Readln;

UNTIL chon = 0;

END. {Ngưng làm toán}

===============================================================

BÀI ÐỌC THÊM TOP

TOP - DOWN STRUCTURED PROGRAMMING

hay

LẬP TRÌNH THEO CẤU TRÚC TRÊN - XUỐNG

--- oOo ---

Nếu các bạn là một người mới bắt đầu khởi sự thực hành lập trình một bài toán nào đó, các bạn sẽ thường tự hỏi: Ta phải bắt đầu bằng việc gì đây? Ðây là một câu hỏi không phải ai cũng trả lời chung được. Tuy nhiên, dựa vào kinh nghiệm thu thập được của những người lập trình tài tử và của những lập trình viên chuyên nghiệp, tác giả Francis Scheid, trong tác phẩm Computer and Programming của mình, đã cho một số lời khuyên sau :

1. Ôn lại những kinh nghiệm đã qua để xem coi vấn đề của bạn có chút gì tương tự đến các vấn đề mà bạn đã từng chạm trán trước đây không;

2. Trước tiên, thử làm một phiên bản đơn giản. Nếu có thể, đưa ngay vào một số trường hợp đặc biệt mà bạn có, nhằm tạo chương trình bạn có một vẻ gì sâu sắc.

3. Chia bài toán ra thành những bài nhỏ, rồi tiếp tục chẻ những bài nhỏ này thành những phần nhỏ hơn, nếu được, chẻ tiếp những phần nhỏ này thành những mảnh nhỏ hơn nữa, sau đó giải quyế từng phần hay mảnh nhỏ này.

Mỗi thuật toán có thể thể hiện bằng lưu đồ (flow chart). Lưu đồ chính là bản đồ lộ trình của thuật toán. Không hẳn tất cả những chuyên viên máy tính phải thực hiện lưu đồ trước khi lập trình nhưng nhờ có lưu đồ mà công việc của bạn trở nên rõ ràng và mang tính logic hơn. Bạn sẽ không cảm thấy bối rối khi cần phải trình bày tiến trình giải toán của bạn cho người khác hiểu. Bạn có thể mất một ít thời gian cho lưu đồ nhưng nó có giá trị hơn cả ngàn từ nếu phải cắt nghĩa thuật toán.

Chúng ta hãy tưởng tượng bài toán to lớn của chúng ta như một cây cổ thụ nhiều cành lá rậm rạp. Ta muốn đốn cây này về nhà làm củi chụm và dĩ nhiên, ta không thể nào chặt ngang gốc cây mà vác về nhà (thí dụ này không có ý khuyến khích phá hoại môi trường đâu nhé ! ). Vậy tại sao ta không tỉa từng cành nhỏ rồi dần dần thanh toán luôn cả cây ? Giải quyết vấn đề của chúng ta cũng vậy. Bạn cứ xem bài toán của chúng ta như một gốc cây lộn ngược đầu. Chia nhỏ bài toán ra thành những vấn đề nhỏ hơn, rồi nhỏ hơn nữa nếu nó còn phức tạp, như minh họa ở hình sau đây:

Trong hình vẽ trên, bài toán được phân thành 4 vấn đề nhỏ hơn là A, B, C và D. Vấn đề B và D có thể giải quyết được ngay. Riêng vấn đề A và C thì lại tiếp tục chia nhỏ hơn nữa để thành những mảnh nhỏ có thể giải quyết được. Ở đây các nhánh cây không dài ngắn như nhau, dễ hiểu bởi vì mức độ phức tạp của mỗi vấn đề không thể như nhau và tùy theo thuật toán cần giải quyết mà ta phân nhỏ nó ra. Bài toán của chúng ta sẽ đi từ vấn đề trừu tượng đến cụ thể. Cách giải quyết như vậy giống như hệ thống phân quyền trong tổ chức chính phủ:

Lập trình cấu trúc là một trường phái lập trình xuất hiện vào thập niên 1970 và đã nhanh chóng được nhiều người hưởng ứng. Ðiểm cơ bản trong lập trình cấu trúc là tổ chức chương trình thành một hệ phân cấp (hierarchy) và phải điều khiển sao cho các mối tương tác giữa các thành phần trong hệ là tối thiểu. Ðây chính là ý tưởng của một phép tinh chế từng bước (stepwise refinement) hay phương pháp chia để trị trong giải bài toán theo cách top-down. Một khi ta đã thực hiện việc phân tích top-down xong, những mảnh bài toán chi tiết nhất sẽ được giải theo cách của 1 trong 3 thành phần thuật toán sau :

* Dòng tuần tự (Sequential Flow) : Trong thuật giải này, các bước giải được thể hiện ở trong một luồng lệnh tuần tự như hình vẽ sau:

* Dòng điều kiện (Conditional Flow): Trong thực tế ở nhiều bài toán máy tính, ta sẽ đi đến việc chọn lựa một trong hai điều kiện. Mỗi điều kiện (Ðúng hoặc Sai) sẽ dẫn đến một quyết định khác nhau. Minh họa ở hình sau:

* Dòng lặp (Repetitive Flow) : Trong nhiều trường hợp, ta cần thực hiện nhiều lần liên tiếp một hay nhiều thao tác cho để khi một điều kiện được thỏa.

Cha đẻ của ngôn ngữ lập trình Pascal là N.Wirth đã phát triển phương pháp tinh chế từng bước, xem nó như một phương pháp khoa học cần thiết cho việc phân tích vấn đề và lập trình.

Khởi đầu, chương trình phải được thể hiện khái quát vấn đề, nêu bậc sự phân tích tổng thể bài toán. Ở từng bước kế tiếp sau đó, sẽ có các giải pháp giải quyết vấn đề một cách chi tiết hơn, mỗi giải pháp như vậy là một sự đặc tả (specification) công việc. Như vậy, từng bước một, ta dần dần tinh chế bài toán. Sự tinh chế này phải hướng đến các thuật toán của ngôn ngữ lập trình. Nếu các bài toán nhỏ trở nên đơn giản thì ta thay nó bằng các câu lệnh. Nếu nó tỏ ra còn phức tạp thì ta xem đó như một thủ tục và tiếp tục tìm cách tinh chế nó.

Trong quá trình tinh chế, cần thiết đưa ra những cách biểu diễn dữ liệu đi song song với cách chi tiết hoá việc giải quyết bài toán. Ðây là một phương pháp khoa học nhưng cũng mang một phần tính nghệ thuật thể hiện sự nhạy bén trong tư duy của người lập trình.

_______________________________________________________________________________________________

CHƯƠNG 8

MỘT SỐ CẤU TRÚC DỮ LIỆU CƠ SỞ

I. KIỂU LIỆT KÊ, KIỂU MIỀN CON.

1. Kiểu vô hướng liệt kê.

2. Kiểu miền con.

II. KIỂU MẢNG, KIỂU CHUỖI.

1. Dữ liệu kiểu mảng.

2. Dữ liệu kiểu chuỗi.

III. KIỂU TẬP HỢP (SET).

1. Ðịnh nghĩa và khai báo.

2. Mô tả một tập hợp.

3. Các phép toán trên tập hợp.

4. Viết và đọc dữ liệu kiểu tập hợp.

IV. KIỂU BẢN GHI (RECORD).

1. Ðịnh nghĩa và khai báo.

2. Truy xuất một Record.

3. Các Record lồng nhau.

4. Câu lệnh WITH.

I. KIỂU LIỆT KÊ, KIỂU MIỀN CON

1. Kiểu vô hướng liệt kê (enumerated scalar type) TOP

Chương trước chúng ta đã đi qua các kiểu dữ liệu đơn giản là các dữ liệu kiểu dữ liệu vô hướng chuẩn (Standard Scalar-type Data) như Integer, Real, Char, Boolean. Các kiểu này đã được định nghĩa sẵn trong mọi chương trình cài đặt trong máy. Ngôn ngữ Pascal cho phép người lập trình có thể tự đặt ra các kiểu vô hướng mới bằng cách tự liệt kê các giá trị của kiểu vô hướng mới và phải khai báo định nghĩa kiểu. Danh sách các giá trị này được đặt trong ngoặc đơn ( ) và được mô tả bằng một tên kiểu (như phần mô tả kiểu TYPE). Kiểu vô hướng theo cách này gọi là kiểu vô hướng liệt kê (Enumerated Scalar Type).

a. Cách khai báo

Có 2 cách khai báo một biến kiểu liệt kê:

+ Khai báo gián tiếp: Ðịnh nghĩa kiểu (dựa vào từ khóa type) trước khi khai biến (var)

TYPE

<tên kiểu liệt kê> = (<danh sách giá trị kiểu liệt kê>) ;

VAR

<danh sách biến> : <tên kiểu liệt kê> ;

Ví dụ 8.1:

TYPE

Days = (Sun, Mon, Tue, Wed, Thu, Fri, Sat) ;

Colors =(Red, Yellow, Green, White, Blue, Black) ;

Subjects = (Mathematics, Physics, Chemistry, Biology) ;

VAR

Ngay : Days ;

MauVe : Colors ;

MonThi, Kiemtra : Subjects ;

+ Khai báo trực tiếp: Kiểu sau biến được định nghĩa trực tiếp.

VAR

<danh sách biến> : (<danh sách giá trị kiểu liệt kê>) ;

Ví dụ 8.2:

VAR

Ngay : (Sun, Mon, Tue, Wed, Thu, Fri, Sat) ;

MauVe : (Red, Yellow, Green, White) ;

Ta có thể gán cho biến các giá trị của kiểu tương ứng:

Ngay := Mon ;

MauVe := Red ;

Biến theo định nghĩa của kiểu nào chỉ nhận giá trị của kiểu đó mà thôi.

Theo khai báo như ví dụ 8.2. ở trên, ta không thể có MauVe := Mon ;

Kiểu vô hướng liệt kê là một kiểu đếm được.

Theo định nghĩa kiểu vô hướng liệt kê, thứ tự danh sách giá trị liệt kê được ngầm đánh số tăng tuyến tính bắt đầu từ số 0 trở đi theo thứ tự từ trái sang phải. Như vậy, ở ví dụ trên: Sun < Mon < Tue < Wed .... và Red < Yellow < Green ...

b. Một số hàm chuẩn áp dụng cho kiểu vô hướng

* Hàm thứ tự ORD (X)

Hàm này cho ta thứ tự của giá trị x trong kiểu vô hướng đếm được. Hàm ORD thực chất là hàm biến đổi một giá trị kiểu vô hướng đếm được sang giá trị kiểu số nguyên.

Theo ví dụ trên:

ORD (Sun) = 0 là Ðúng vì Sun có thứ tự là 0

ORD (Mon) = 1 là Ðúng vì Mon có thứ tự là 1

ORD (Green) = 3 là Sai vì Green có thứ tự là 2

ORD (n) = n trong đó n là một giá trị kiểu Longint

* Hàm PRED (X)

Hàm này cho giá trị đứng trước x trong định nghĩa kiểu của x.

Theo ví dụ trên :

PRED (Mon) = Sun

PRED (Green) = Yellow

PRED (n) = n - 1

* Hàm SUCC (X)

Hàm này cho giá trị đứng sau x trong định nghĩa kiểu của x.

Theo ví dụ trên:

SUCC (Mon) = Tue

SUCC (Green) = White

SUCC (n) = n + 1

* Hàm chuyển một số nguyên thành một giá trị vô hướng

Tên hàm này chính là tên kiểu vô hướng mà ta đã khai báo trước.

Theo ví dụ trên:

Days(2) = Tue

Colors(3) = White

LONGINT (n) = n

c. Viết ra và đọc vào kiểu liệt kê

Viết và đọc theo kiểu liệt kê thì khác với kiểu vô hướng chuẩn.

* Viết ra kiểu liệt kê

Thủ tục Write và Writeln chỉ chấp nhận đưa ra các giá trị thuộc kiệu vô hướng chuẩn (Real, Integer, Byte, Char, Boolean) mà không chấp nhận viết ra một giá trị kiểu vô hướng liệt kê, ví dụ cách viết sau là không đúng:

Writeln(Color(4)) Writeln(Red) Writeln(Days)

mà chỉ có thể chấp nhận nếu viết:

Writeln (Char(78)) vì Char(78) = N là giá trị vô hướng chuẩn.

Ðể viết ra một giá trị của biến vô hướng liệt kê, ta có thể áp dụng thủ thuật sau:

IF MauVe = Red THEN Writeln('Red') ;

* Ðọc vào kiểu liệt kê

Thủ tục Read và Readln cũng chỉ chấp nhận đọc vào một giá trị kiểu vô hướng chuẩn mà không chấp nhận đọc trực tiếp các giá trị kiểu vô hướng liệt kê, ví dụ không thể đọc Readln(Days). Ðể đọc vào một giá trị kiểu liệt kê ta có thể dùng phương pháp sau: đọc số thứ tự của giá trị biến vô hướng rồi biến đổi kiểu dữ liệu thêm:

Ví dụ 8.3:

TYPE Days = (Sun, Mon, Tue, Wed, Thu, Fri, Sat) ;

VAR i : Integer ;

BEGIN

Write('Nhập số từ 0 . .6 tương ứng cho ngày:'); Readln(i) ;

Case Days(i) of

Sun: writeln('Ngày Chủ nhật');

Mon: writeln('Ngày thứ hai');

Tue: writeln('Ngày thứ ba');

Wed: writeln('Ngày thứ tư');

Thu: writeln('Ngày thứ năm');

Fri: writeln('Ngày thứ sáu');

Sat: writeln('Ngày thứ bảy');

Else writeln('Nhập sai');

end;

Readln;

END.

Mục 2 ở phần II ở phía sau, sẽ giới thiệu chuỗi String, ta có thể dùng thủ thuật sau để đọc kiểu liệt kê:

Ví dụ 8.4: Readln(St) ;

IF St = 'Mon' THEN Ngay := Mon ;

2. Kiểu miền con (Sub-range type) TOP

a. Khái niệm

Khi khai báo một số trường hợp, ví dụ Tuổi của người hoặc Ðiểm thi học sinh, nếu ta viết:

VAR

TuoiTho : Integer ; {Integer có miền xác định -32 768 .. 32 767}

Hay Diem : Real ; {Real có miền xác định 2.9 E-39 .. 1.7 E38}

Nếu viết như vậy sẽ tốn ô nhớ vì Integer có kích thước 2 bytes hoặc Real có kích thước đến 6 bytes. Làm như vậy sẽ không cần thiết vì Tuổi con người chỉ biến thiên trong khoảng từ 0 đến 200 là lớn nhất và điểm thi học sinh thì chỉ trong khoảng từ 0 đến 10 chẳng hạn.

Trong Pascal cho phép ta xác định một biến lấy giá trị trong một khoảng nào đó được giới hạn ở một hằng cận dưới (first data item) và một hằng cận trên (last data item). Hai giá trị này phải cùng một kiểu vô hướng đếm được và hằng cận trên có giá trị lớn hơn hằng cận dưới. Khai báo như vậy gọi là khai báo kiểu miền con (Sub-range type) và biến của nó chỉ chiếm 1 byte trong ô nhớ mà thôi. Trong lúc chạy chương trình, ta có thể kiểm tra giá trị của biến không được vượt ra khỏi giới hạn của khoảng con.

b. Cách khai báo

Miền con là một tập hợp con của một kiểu đếm được. Có 2 cách khai báo:

+ Khai báo gián tiếp:

TYPE

<Tên kiểu miền con> = <hằng cận dưới> .. <hằng cận trên> ;

VAR

<danh sách biến> : < Tên kiểu miền con> ;

Ví dụ 8.5:

TYPE

TuoiTho = 0 .. 200 ;

VAR Tho : TuoiTho ;

+ Khai báo trực tiếp:

VAR

<danh sách biến> : <hằng cận dưới> .. <hằng cận trên> ;

Ví dụ 8.6:

VAR Tuoi : 0 .. 200 ;

II. KIỂU MẢNG, KIỂU CHUỖI

Pascal có 4 kiểu cấu trúc dữ liệu là kiểu mảng (ARRAY), tập hợp (SET), bản ghi (RECORD) và tập tin (FILE). Sau đây ta lần lượt tìm hiểu từng kiểu cấu trúc.

1. Dữ liệu kiểu mảng (Array-Type Data) TOP

Một mảng dữ liệu là một tập hợp số hữu hạn phần tử có giống như các biến, có cùng kiểu, gọi là kiểu cơ bản.

Mảng được được tổ chức theo một trật tự xác định. Số phần tử của mảng được khai báo ngay từ khi định nghĩa ra mảng.

a. Mảng một chiều (One-Dimensional Array)

Mảng một chiều có thể được hiểu như một danh sách các phần tử (theo cột), có cùng kiểu. Mỗi phần tử của mảng được xác định được truy nhập trực tiếp thông qua tên mảng cùng với chỉ dẫn truy nhập được để giữa hai ngoặc vuông [ ].

Ví dụ 8.7:

List là một mảng 1 chiều có n phần tử. Các phần tử của List có thể mang các tên List[1], List[2], List[3], ..., List[n], và có thể minh họa như hình sau:

List[1] List[2] List[3] List[4] ......... List[n]

Hình 8.1: Minh họa mảng một chiều

+ Khai báo gián tiếp:

TYPE

<Kiểu mảng> = ARRAY [Kiểu chỉ số ] OF <Kiểu phần tử > ;

VAR

<Danh sách biến> : Kiểu mảng ;

+ Khai báo trực tiếp :

VAR

< Danh sách biến > : ARRAY [ Kiểu chỉ số] OF < Kiểu phần tử > ;

* Chú ý: Kiểu chỉ số phải là kiểu rời rạc (đếm được).

Ví dụ 8.8:

TYPE

KM1 = ARRAY [1.. 100] OF INTEGER ;

KM2 = ARRAY [1 .. 20 ] OF CHAR ;

DAY = (Sun, Mon, Tue, Wed, Thu, Fri, Sat) ;

VAR

TUOI : KM1 ;

TEN : KM2 ;

NGAY : ARRAY [DAY] OF BOOLEAN ;

Ý nghĩa:

- KM1 là kiểu mảng gồm 100 phần tử được đánh số từ 1 đến 100 thông qua kiểu chỉ dẫn là một miền con các số nguyên từ 1 .. 100. TUOI là biến có kiểu là KM1.

- KM2 là kiểu mảng gồm 20 phần tử đánh số từ 1 .. 20 có kiểu là các ký tự. Biến TEN có kiểu là KM2.

- NGAY là một biến mảng gồm 7 phần tử kiểu Boolean được đánh dấu qua kiểu chỉ dẫn là tên của 7 ngày trong tuần.

Chú ý:

Khi khai báo mảng, kiểu chỉ dẫn chỉ có thể là:

- Kiểu miển con của các loại dữ liệu vô hướng đếm được như ký tự, số nguyên

- Kiểu liệt kê do người viết định nghĩa (như NGAY trong tuần)

- Kiểu Boolean

Kiểu chỉ dẫn không thể là kiểu không đếm được như REAL

Viết như sau là SAI : X1 : ARRAY [Real] OF Integer ;

Ta cũng không thể khai báo như: X2 : ARRAY [Integer] OF Integer ;

Mặc dầu Integer là kiểu vô hướng đếm được do giới hạn của vùng nhớ dành cho dữ liệu, số lượng phần tử của 1 mảng cũng bị hạn chế tùy theo kích thước của kiểu dữ liệu của các phần tử, ta nên dùng kiểu miền con để khai báo số phần tử của mảng.

+ Truy xuất các phần tử của mảng:

Mỗi phần tử của mảng được truy xuất thông qua Tên Biến Mảng cùng với chỉ số của mảng trong dấu ngoặc vuông [ ]. Ví dụ tên biến mảng là A, khi viết A[7], ta hiểu nó là phần tử thứ 7 của mảng A.

Ví dụ 8.9: Lập trình giải một bài toán tính trung bình một dãy số x[i] :

x[1], x[2], x[3], ... , x[n]

sau đó tiếp tục tính độ lệch (deviation) của từng phần tử so với trị trung bình, theo công thức:

độ_lệch = x[i] - trung_bình

Giả sử dãy số của chúng ta có giới hạn n = 100 phần tử trở lại, n là một biến số để khai báo số phần tử muốn tính . Sau đó ta lần lượt nhập tính giá trị của phần tử kiểu số thực (real) từ phần tử thứ 1 đến phần tử thứ n. Trong chương trình sẽ tạo ra một mảng 1 chiều x với n các phần tử. Tính trung bình của n phần tử và độ lệch. In kết quả ra màn hình.

PROGRAM Average_deviations ;

{ Nhập n số phần tử kiểu số thực, tính trị trung bình của chúng, sau đó tính tiếp độ lệch của từng phần tử số so với trị trung bình }

VAR

n, count : integer ;

sum, average, deviation : real ;

x : ARRAY [1 .. 100] OF real ;

BEGIN

(* Nhập số phần tử và tính trung bình*)

Write (' Nhập bao nhiêu số n để tính trung bình ? ') ;

Readln (n) ;

Writeln ;

sum := 0 ;

FOR count := 1 TO n DO

BEGIN

Write ( ' i = ', count : 3, ' x = ' ) ;

Readln (x [count] ) ;

sum := sum + x[count];

END ;

average := sum/n ;

Writeln (' Trung bình của dãy số là = , average ') ;

Writeln ;

(* Tính độ lệch so với trị trung bình *)

FOR count := 1 TO n DO

BEGIN

deviation := x[count] - average ;

Write ( ' i = ', count : 3, ' x = ', x[count] ) ;

Writeln (' Ðộ lệch d = , deviation ');

END ;

Readln;

END.

Giả sử, ta nhập vào 5 số hạng (các số có gạch dưới là phần của người nhập):

x[1] = 3.0 x[2] = -2.0 x[3] = 12.0 x[4] = 4.4 x[5] = 3.5

Khi chạy chương trình (nhấn Ctrl + F9), trên màn hình ta sẽ thấy :

Nhập bao nhiêu số n để tính trung bình ? 5

i = 1 x = 3.0

i = 2 x = -2.0

i = 3 x = 12.0

i = 4 x = 4.4

i = 5 x = 3.5

Trung bình của dãy số là = 4. 1800000E+00

i = 1 x = 3. 0000000E+00 Ðộ lệch d = - 1. 1800000E+00

i = 2 x = -2. 0000000E+00 Ðộ lệch d = - 6. 1800000E+00

i = 3 x = 1. 2000000E+00 Ðộ lệch d = 7. 8200000E+00

i = 4 x = 4. 4000000E+00 Ðộ lệch d = 2. 2000000E - 01

i = 5 x = 3. 5000000E+00 Ðộ lệch d = - 6. 8000000E - 01

Ta có thể định khoảng chừa kết quả và phần lẻ thập phân, dùng lệnh : m : n

Ví dụ 8.10: Sắp xếp một dãy số theo thứ tự từ nhỏ đến lớn

Tiến trình của bài toán:

- Giả sử chuỗi số của ta có n phần tử . Lần lượt cho chương trình đọc giá trị của các phần tử nhập được.

- Một thủ tục (Procedure) sẽ làm công việc sắp xếp như sau : đầu tiên đưa phần tử thứ nhất so sánh với các phần tử tiếp theo, nếu nó lớn hơn phần tử so sánh thì đem đổi chổ giá trị của hai phần tử với nhau. Sau đó tiếp tục đem phần tử thứ 2 so sánh các phần tử tiếp theo theo trình tự như vậy, ... và cứ như thế cho đến phần tử thứ n - 1.

- In kết quả ra màn hình

Chương trình Pascal như sau:

PROGRAM Reorder ;

(* Sắp xếp một mảng các phần tử số thực từ nhỏ đến lớn*)

VAR n, i, loc: 1 .. 100 ;

x : ARRAY [1 .. 100] OF real ;

temp : real ;

PROCEDURE interchange ;

(* Ðổi chỗ các phần tử mảng từ nhỏ đến lớn*)

BEGIN

FOR loc := 1 TO n-1 DO

FOR i := loc + 1 TO n DO

IF x[i] < x [loc] THEN

BEGIN

temp := x[loc] ;

x[loc] := x[i] ;

x[i] := temp ;

END ;

END ;

BEGIN

Write (' Có bao nhiêu phần tử số ? ') ; Readln (n) ;

FOR i := 1 TO n DO

BEGIN

Write ( ' x[ ', i : 3, '] = ? ' ) ;

Readln( x[i] ) ;

END ;

interchange ;

Writeln ;

Writeln (' Số liệu đã sắp xếp : ') ;

Writeln ;

FOR i := 1 TO n DO

Writeln ( 'x[ ', i : 3, ' ] = ', x[i] : 4 : 1 ) ;

Readln;

END.

Khi chạy chương trình, giả sử ta có 5 số liệu như phần nhập :

(các số có gạch dưới là phần nhập từ bàn phím)

Có bao nhiêu phần tử số ? 5

x[ 1] = ? 4. 7

x[ 2] = ? - 2. 3

x[ 3] = ? 12. 9

x[ 4] = ? 8. 8

x[ 5] = ? 6. 0

Kết quả là :

Số liệu đã sắp xếp :

x[ 1] = ? - 2. 3

x[ 2] = ? 4. 7

x[ 3] = ? 6. 0

x[ 4] = ? 8. 8

x[ 5] = ? 12. 9

b. Mảng nhiều chiều (Multi-Dimensional Array)

Trong một số bài toán thực tế, người ta sử dụng các mảng nhiều hơn 1 chiều, gọi là mảng nhiều chiều.

Ví dụ 8.11: Phòng Ðào tạo quản lý điểm của sinh viên. Trong khoá 22 chẳng hạn, người ta tạo ra một mảng 2 chiều: ví dụ một chiều là số thứ tự của sinh viên, chiều còn lại là các môn học (dạng kiểu vô hướng liệt kê), ta có thể hình dung dạng của mảng ghi điểm (tên mảng là ghi_diem) như sau:

Lưu ý: Thực tế, danh sách tên sinh viên lưu lại trong máy tính thường được ghi bằng cách gán mã số sinh viên (coding) cho mỗi sinh viên ngay từ năm đầu vào học.

Với ví dụ trên, muốn nhập điểm một sinh viên nào đó ta phải khai báo 2 tham số là số thứ tự sinh viên và môn học.

Tương tự, cũng với các khoá kế tiếp theo học những môn như vậy, ta sẽ tạo ra mảng nhiều chiều như hình vẽ minh họa sau:

Trong trường hợp này, muốn biết điểm một sinh viên nào đó ta phải khai báo 3 tham số: Khoá học, số thứ tự sinh viên và môn học, chẳng hạn:

ghi_diem[K22,0001,AV] nhập điểm 10,...

Khai báo cũng có 2 cách như đối với mảng 1 chiều:

+ Khai báo gián tiếp:

TYPE

<Kiểu mảng> = ARRAY [Kiểu_chỉ_số_1, ..., Kiểu_chỉ_số_n] OF <Kiểu phần tử>;

VAR

<Danh sách biến>:<Kiểu mảng>;

Ví dụ 8.12:

TYPE matrix = ARRAY [1 .. 20, 1 .. 30] OF integer ;

VAR A:matrix;

Lệnh trên khai báo một kiểu tên matrix. Ðây là một mảng 2 chiều, chiều thứ nhất có các chỉ số từ 1 đến 20, chiều thứ hai có các chỉ số từ 1 đến 30, tổng cộng ta có (20 x 30) phần tử số nguyên. Và ta có một biến A là biến có kiểu matrix.

Ví dụ trên cũng có thể được khai báo tương đương với:

TYPE matrix = ARRAY [1 .. 20] OF ARRAY [1 .. 30] OF integer ;

VAR A:matrix;

+ Khai báo gián tiếp:

VAR

<Danh sách biến>: ARRAY [Kiểu_chỉ_số_1, ..., Kiểu_chỉ_số_n] OF <Kiểu phần tử>;

Khai báo một biến A có 5 dòng và 10 cột kiểu phần tử là Integer như sau:

VAR A : ARRAY [1 .. 5, 1 .. 10] OF integer ;

+ Truy xuất các phần tử mảng:

Tương tự như cách truy xuất phần tử của mảng 1 chiều, mảngg nhiều chiều cũng được truy xuất thông qua tên biến mảng kết hợp với các chỉ số của nó được đặt trong cặp dấu ngoặc vuông.

Mảng 2 chiều là một ma trận, như ví dụ trên ta có một ma trận 5 dòng và 10 cột. Các phần tử của ma trận A được ký hiệu là a[i,j] với i là vị trí cột và j là dòng. Khi viết a[2, 7] thì hiểu đây là phần tử ở dòng 2 và cột 7.

Trong Pascal, ta có thể viết a[i,j] thành a[i] [j] với ý nghĩa hoàn toàn như nhau.

Chú ý: Trên nguyên tắc, ta có thể khai báo một mảng có đến 255 chiều. Tuy vậy, một điều cần lưu ý là kích thước bộ nhớ của máy tính có hạn nên thường chỉ khai báo mảng từ 1 đến 3 chiều. Khai biến quá nhiều thì phải cần máy lớn hơn.

Chẳng hạn khi báo 1 mảng [1.. 10] các phần tử số nguyên đã lấy 10 bytes bộ nhớ

- Mảng 2 chiều 10 x 10 = 100 bytes bộ nhớ.

- Mảng 3 chiều 10 x 10 x 10 = 1 000 bytes bộ nhớ

- Mảng 4 chiều 10 x 10 x 10 x 10 = 10 000 bytes bộ nhớ

- Mảng 5 chiều 10 x 10 x 10 x 10 x 10 = 100 000 bytes bộ nhớ

- v.v...

Ví dụ 8.13:

Viết một chương trình Pascal để đọc một bảng các số thực được nhập vào máy tính dưới dạng một mảng 2 chiều. Tính tổng các giá trị số theo hàng và theo cột. Kết quả được in ra màn hình theo vị trí hàng và cột tương ứng.

Trước tiên, ta bắt đầu bằng định nghĩa các biến:

table = mảng 2 chiều chứa số thực dưới dạng bảng gồm các số nhập và kết quả

nrows = một biến số nguyên chỉ số hàng

ncols = một biến số nguyên chỉ số cột

row = một số đếm nguyên chỉ số hàng

col = một số đếm nguyên chỉ số cột

Ðể đơn giản, chúng ta giả sử rằng kích thước số liệu nhập vào bảng tính không vượt quá 10 hàng và 10 cột. Ta sẽ thêm vào 1 hàng cộng phía dưới và 1 cột cộng bên phải vào bảng để ghi kết quả tính cộng các phần tử hàng và cột tương ứng. Như vậy, mảng 2 chiều của chúng ta sẽ trở thành mảng sẽ được in ra có số hàng là (nrows + 1) và số cột là (ncols +1). Do vậy, ta phải khai báo biến table là 1 mảng 2 chiều số nguyên có tối đa 11 cột và 11 hàng.

Ðể dễ theo dõi chương trình, ta thực hiện cấu trúc module khi viết chương trình bằng cách tiến hành làm các thủ tục procedure cho đọc số liệu, tính tổng các phần tử theo hàng, tính tổng các phần tử theo cột và in ra màn hình bảng kết quả. Các thủ tục này sẽ có tên tương ứng là readinput, rowsums, columsums và writeoutput.

Thuật toán logic yêu cầu cho mỗi thủ tục là cách khai báo thẳng trước (straightforward), chú ý rằng trong mỗi thủ tục ta có một vòng lặp đôi (double loop). Ví dụ, để đọc số liệu ở bảng gốc, ta sẽ phải làm một vòng lặp đôi sau:

FOR row := 1 TO nrows DO

BEGIN

FOR col := 1 TO ncols DO readln( table[row, col] ) ;

Writeln;

END ;

Câu lệnh Writeln để báo chương trình nhảy tới dòng kế.

Tương tự, vòng lặp sau được viết để tính tổng các phần tử theo hàng:

FOR row := 1 TO nrows DO

BEGIN

table [row, ncols + 1] := 0 ;

FOR col := 1 TO ncols DO

table [row, ncols + 1] := table [row, ncols + 1] + table [row, col];

END ;

Tương tự, cấu trúc vòng lặp đôi cũng được dùng để tính tổng các phần tử cột và in ra bảng kết quả cuối cùng.

Sau đây là chương trình Pascal của bài toán trên:

PROGRAM Tongbang ;

{đọc một bảng số, tính tổng từng cột và hàng của cá bảng}

VAR

row, col : 1 .. 11 ;

nrows, ncols : 1 .. 10 ;

table : ARRAY [1 .. 11, 1 .. 11] OF real ;

PROCEDURE Rowsums ; {cộng các phần tử theo cột bên trong mỗi hàng }

BEGIN

FOR row := 1 TO nrows DO

BEGIN

table [row,ncols+1] := 0 ;

FOR col := 1 TO ncols DO

table[row, ncols+1] := table[row, ncols+1] + table[row,col];

END ;

END ;

PROCEDURE Columnsums ; {cộng các phần tử theo hàng bên trong từng cột }

BEGIN

FOR col := 1 TO ncols DO

BEGIN

table [nrows+1, col] := 0 ;

FOR row := 1 TO nrows DO

table[nrows+1,col] := table[nrows+1,col] + table[row,col];

END ;

END ;

PROCEDURE Readinput ; {đọc các phần tử của bảng }

BEGIN

Write(' Nhập số hàng (1 .. 10) ? ') ;Readln(nrows) ;

Write(' Nhập số cột (1 .. 10) ? ') ;Readln(ncols) ;

FOR row := 1 TO nrows DO

BEGIN

Writeln (' Nhập số liệu hàng số , row :2') ;

FOR col := 1 TO ncols DO readln(table [row, col] ) ;

END ;

END ;

PROCEDURE Writeoutput ; { In ra bảng số liệu và kết quả tính tổng }

BEGIN

Writeln('Bảng số liệu và kết quả tính tổng các phần tử theo hàng và cột ');

Writeln('============================================= ');

Writeln;

FOR row := 1 TO nrows + 1 DO

BEGIN

FOR col := 1 TO ncols+1 DO Write (table [row,col] : 6 : 1) ;

Writeln;

END ;

END ;

BEGIN { Thân chương trình chính }

Readinput ;

Rowsums ;

Columnsums ;

Writeoutput;

END. { Chấm dứt chương trình }

Giả sử, ta có bảng số liệu sau :

2.5 -6.3 14.7 4.0

10.8 12.4 -8.2 5.5

-7.2 3.1 17.7 -9.1

Khi chạy chương trình, ta có (số có gạch dưới là số của người thử chương trình):

Nhập số hàng (1 .. 10 ) ? 3

Nhập số cột (1 .. 10) ? 4

Nhập số liệu hàng số 1

2.5 -6.3 14.7 4.0

Nhập số liệu hàng số 2

10.8 12.4 -8.2 5.5

Nhập số liệu hàng số 3

-7.2 3.1 17.7 -9.1

Chương trình sẽ tính tổng các giá trị ở hàng và cột, xong in ra màn hình kết quả:

Bảng số liệu và kết quả tính tổng các phần tử theo hàng và cột

2.5 -6.3 14.7 4.0 14.9

10.8 12.4 -8.2 5.5 20.5

-7.2 3.1 17.7 -9.1 4.5

6.1 9.2 24.2 0.4 0.0

Ta có thể kiểm tra kết quả ở các hàng và cột.

2. Dữ liệu kiểu chuỗi (String Type Data) TOP

Một chuỗi dữ liệu là một loạt các ký tự được định nghĩa bằng từ khoá STRING theo sau là số ký tự cực đại có thể có của chuỗi ký tự. String là một kiểu cấu trúc được thêm vào trong Turbo Pascal.

a. Khai báo

Chúng ta có thể khai báo kiểu chuỗi ký tự String gián tiếp hoặc trực tiếp. Khai báo gián tiếp là khai kiểu trước rồi sau đó mới khai báo biến. Cách khai báo trực tiếp là khai thẳng biến số. Chiều dài tối đa của chuỗi ký tự phải là một hằng nguyên và được đặt trong dấu ngoặc vuông [ ]. Trường hợp không khai báo thì chương trình sẽ lấy giá trị mặc nhiên là 255 ký tự

+ Khai báo gián tiếp

TYPE

<Tên kiểu String> = STRING [hằng nguyên] ;

VAR

<Tên biến> : <Tên kiểu String> ;

Ví dụ 8.14:

TYPE

TenSV = STRING [25] ; {định độ dài tối đa là 25}

Diachi = STRING; {mặc nhiên có độ dài tối đa là 255}

VAR

HT : TenSV ;

DC : Diachi ;

+ Khai báo trực tiếp

VAR

<Tên biến> : STRING [hằng nguyên] ;

Ví dụ 8.15:

VAR

HT : STRING [25] ;

DC : STRING;

Chuỗi ký tự sẽ chiếm số byte trong bộ nhớ bằng số ký tự lớn nhất đã khai báo trước cộng thêm 1 byte đầu tiên chứa số ký tự hiện có của chuỗi ký tự.

Ví dụ 8.16:

TYPE DH = STRING[10] ;

VAR CT : DH ;

và nếu ta gán CT := CAN THO;

thì CT sẽ được cấp phát 1 + 10 = 11 ô nhớ (byte) liên tục, với hình ảnh sau :

Chú ý:

- Ðộ dài của chuỗi ký tự CT là 7 ký tự mặc dầu độ dài lớn nhất cho phép là 10.

- Vì ta dùng 1 byte để chứa chiều dài nên string chỉ có tối đa là 255 ký tự.

b. Các thao tác trên chuỗi

+ Phép gán

Giống như phép gán trong các kiểu vô hướng khác, phép gán chuỗi là lệnh gắn một biến với một biểu thức ký tự để trong cặp dấu nháy đơn

Cú pháp:

<Tên biến> := Biểu thức ký tự ;

Ví dụ 8.17:

HT := Lê Văn Hai ;

DC := Số 12/4 đường Trần Hưng Ðạo, TP. Cần thơ ;

+ Phép cộng

Phép cộng là thuật toán nối các chuỗi lại với nhau bằng dấu cộng (+).

Ví dụ trên nếu ghép HT + DC thì ta sẽ được:

Lê Văn Hai Số 12/4 đường Trần Hưng Ðạo, TP. Cần thơ

Ghi chú: Không có phép trừ, nhân, chia trong chuỗi ký tự.

+ Các phép so sánh

Các so sánh gồm có bằng nhau =, lớn hơn >, lớn hơn hoặc bằng >=, khác nhau <>, nhỏ hơn <, nhỏ hơn hoặc bằng <=

Khi so sánh 2 chuỗi ký tự thì các ký tự được so sánh từng cặp một từ trái sang phải theo giá trị của bảng mã ASCII. Có 2 khả năng xảy ra khi so sánh:

- Nếu 2 chuỗi có độ dài khác nhau nhưng số ký tự giống nhau cho đến độ dài chuỗi ngắn nhất thì chuỗi ngắn nhỏ hơn chuỗi dài.

Ví dụ 8.18: 'Nation' < 'National ''Lan' < 'Lang'

- Nếu 2 chuỗi có độ dài và nội dung giống nhau thì bằng nhau.

Ví dụ 8.19: 'Hello' = 'Hello'

Ghi chú: Chuỗi rổng (null string, viết là '') là chuỗi không có chứa gì cả. Nó có giá trị nhỏ hơn mọi string khác rỗng.

Vì vậy: 'A' >'' và chr(32)> ''

+ Câu lệnh Read và Readln

Hai câu lệnh này đối với chuỗi cũng tương tự như đối với các kiểu vô hướng khác, nhưng cần lưu ý:

- Lệnh Read và Readln chỉ cho phép đọc tối đa 127 ký tự một chuỗi nhập từ bàn phím mặc dầu chiều dài tối đa của một chuỗi có thể đến 255 ký tự.

- Nếu ta đọc một lúc nhiều biến theo kiểu Read(biến1, biến2, ..., biếnN) ( hoặc Readln(biến1, biến2, ..., biếnN)) thì có thể bị nhầm lẫn khi ta nhập giá trị có độ dài vượt quá độ dài tối đa của biến1 thì phần vượt sẽ được gán cho biến2. Ngược lại, nếu ta nhập giá trị ít hơn độ dài của biến1 thì chương trình lại lấy các giá trị của biến2 gán thêm cho biến1 kể cả khoảng trống. Do vậy, cách tốt nhất là đối với biến kiểu String chỉ nên nhập mỗi lần 1 biến.

Ví dụ 8.20: Nên tránh viết kiểu Read(TenSV, Diachi); mà nên viết :

Read(TenSV) ;

Read(Diachi) ;

hoặc:

Readln(TenSV) ;

Readln(Diachi) ;

- Ðộ dài thực tế của chuỗi là độ dài thực tế khi ta đọc vào từ bàn phím mặc dầu trước đó ta có khai báo độ dài chuỗi. Nếu ta gõ Enter mà không gõ ký tự nào trước đó thì mặc nhiên chương trình hiểu đó là một chuỗi rỗng (null string hay st = '').

+ Câu lệnh Write và Writeln

Tương tự như trên nhưng cần một số lưu ý về cách viết:

- Nếu viết Write(st) hoặc Writeln(st) gọi là cách viết không qui cách thì mỗi ký tự sẽ chiếm 1 vị trí trên màn hình.

- Nếu viết Write(st : n) hoặc Writeln(st : n) gọi là cách viết theo qui cách, với n là số nguyên, thì màn hình sẽ dành n vị trí để viết chuỗi st theo lối canh trái nếu n> 0 và ngược lại theo lối canh phải nếu n < 0.

- Một số chuỗi mà trong đó có dấu như là một chữ viết tắt, ví dụ như câu: Hes an Intal staff (Ông ta là một nhân viên quốc tế) thì nơi có dấu phải viết thành (đây là 2 dấu nháy đơn chứ không phải là 1 dấu nháy kép ).

Ta viết:

Writeln ( ' He ''s an Int''al staff ') ;

c. Các thủ tục và hàm chuẩn xử lý chuỗi ký tự

Chuỗi ký tự được dùng khá phổ biến trong lập trình nên Turbo Pascal đã đưa sẵn vào một số thủ tục và hàm chuẩn để xử lý chuỗi ký tự.

* Thủ tục xóa DELETE (St, Pos, Num)

Ý nghĩa: Xóa khỏi chuỗi St một số ký tự là Num bắt đầu từ vị trí Pos tính từ trái sang.

Ví dụ 8.21: VAR st : string [20];

BEGIN

St := ' BÀ BA BÁN BÁNH BÒ '; Writeln (St) ;

DELETE (St, 10, 4); Writeln(St); Readln ;

END.

Khi chạy chương trình, ta sẽ thấy trên màn hình:

BÀ BA BÁN BÁNH BÒ

BÀ BA BÁN BÒ

* Thủ tục INSERT (Obj, St, Pos)

Ýï nghĩa: Chèn chuỗi Obj xen vào chuỗi St kể từ vị trí Pos tính từ bên trái.

Ví dụ 8.22: VAR st : string [25];

BEGIN

St := 'BÀ BA BÁN BÁNH BÒ' ; Writeln (St) ;

INSERT ( BỤNG BỰ , St, 6); Writeln(St); Readln ;

END.

Khi chạy chương trình, ta sẽ thấy trên màn hình:

BÀ BA BÁN BÁNH BÒ

BÀ BA BỤNG BỰ BÁN BÁNH BÒ

* Thủ tục STR (S [: n[: m]], St)

Ý nghĩa: Ðổi giá trị số S thành chuỗi rồi gán cho St, Giá trị n:m nếu có sẽ là số vị trí và số chữ số thập phân của S.

Ví dụ 8.23: VAR S: real;

St: string[10];

BEGIN

S:= 12345.6718;

Writeln(S:5:2);

Str(S:6:2:st);

Readln;

END.

Kết quả trên màn hình:

12345.67 {Ðây là số }

12345.67 {Ðây là chuỗi}

* Thủ tục VAL(St, S, Code)

Ý nghĩa: Ðổi chuỗi số St (biểu thị một số nguyên hoặc số thực) thành số (số nguyên hoặc số thực) và gán giá trị này cho S. Code là số nguyên dùng để phát hiện lỗi: nếu đổi đúng thì Code có giá trị = 0, nếu sai do St không biểu diễn đúng số nguyên hoặc số thực thì Code sẽ nhận giá trị bằng vị trí của ký tự sai trong chuỗi St.

Ví dụ 8.24: VAR St : String[10];

SoX : real;

maloi: integer;

BEGIN

St:= '123.456' ;

VAL(St,SoX,maloi) ;

Writeln('Số X = , SoX :5:2, và mã lỗi = , maloi) ;

Readln;

St:='123.XXX ';

VAL(St,SoX,maloi);

Writeln('St = 123.XXX không đổi thành số được !');

Writeln('Sai lỗi ở vị trí thứ ' , maloi); Readln;

END.

Khi chạy, ta sẽ thấy trên màn hình:

123.45 và maloi = 0

St = 123.XXX không đổi thành số được !

Sai lỗi ở vị trí thứ 5

* Hàm LENGTH (St)

Ý nghĩa: Cho kết quả là một số nguyên chỉ độ dài của chuỗi ký tự St.

Ðể viết 1 chuỗi ký tự ở trung tâm màn hình, ta có thể dùng thủ thuật viết chuỗi là (80 - lenght(st)) div 2

Ví dụ 8.25:

USES CRT;

VAR St : String[80];

BEGIN

ClrScr ;

Write(' Nhập vào một câu : '); Readln(St) ;

Gotoxy(80 - Lenght(St)) div2, 12);

Writeln(St) ;

Readln ;

END.

* Hàm COPY (St, Pos, Num)

Ý nghĩa: Cho kết quả là một chuỗi ký tự mới có được bằng cách chép từ chuỗi St, bắt đầu từ vị trí Pos và chép Num ký tự.

Nếu vị trí Pos lớn hơn chiều dài của chuỗi St thì hàm COPY sẽ cho một chuỗi rỗng. Nếu giá trị của vị trí Pos và số ký tự Num (Pos + Num) lớn hơn chiều dài của chuỗi St thì hàm COPY chỉ nhận các ký tự nằm trong chuỗi St.

Ví dụ 8.26:

VAR St1, St2 : string[25] ;

BEGIN

St1 := 'UNIVERSITY OF CANTHO : 1966 - 1996' ;

St2 := COPY (St1, 15, 6) ;

END.

Như vậy, giá trị của biến St2 bây giờ là CANTHO.

* Hàm CONCAT (St1, St2, ..., StN)

Ý nghĩa: Cho kết quả là một chuỗi mới được ghép theo thứ tự từ các chuỗi St1, St2, ..., StN. Hàm này giống như phép cộng các chuỗi. Chuỗi mới cũng sẽ không được vượt quá 255 ký tự.

* Hàm POS (Obj, St) :

Ý nghĩa: Cho kết quả là vị trí đầu tiên của chuỗi Obj trong chuỗi St. Nếu không tìm thấy thì hàm POS cho giá trị 0.

Ví dụ 8.27:

nếu St := 1234567890, nếu Obj := 456 thì POS (Obj, St) = 4 còn POS(4X, St)=0

d. Truy xuất từng ký tự trong chuỗi

Ta có thể truy xuất đến từng ký tự trong chuỗi với tên biến và chỉ số trong dấu ngoặc vuông [ ] như truy xuất các phần tử của mảng. Ví dụ với chuỗi St thì St[i] là ký tự thứ i trong chuỗi St, dĩ nhiên . Chỉ số i chạy dài từ 1 đến độ dài lớn nhất của chuỗi ký tự.

Ví dụ 8.28:

PROGRAM DoiChu;

VAR St:String;

i: integer;

BEGIN

Write('Hãy nhập tên của bạn : ');

Readln(St);

FOR i:= 1 TO Length(St) DO

St[i] := Upcase(St[i]);

(*Hàm Upcase đổi ký tự thành chữ in hoa*)

Writeln;

Writeln(St);

Readln;

END.

III. KIỂU TẬP HỢP (SET)

1. Ðịnh nghĩa và khai báo TOP

Một tập hợp (SET) bao gồm một số các phần tử có cùng bản chất kiểu là kiểu cơ bản. Trong Turbo Pascal và IBM Pascal, số phần tử tối đa trong một tập hợp là 256. Kiểu cơ bản có thể là kiểu vô hướng liệt kê, kiểu miền con hoặc kiểu Char, không được là số thực. Khái niệm tập hợp trong Pascal tương tự như khái niệm tập hợp trong toán học.

+ Khai báo gián tiếp

TYPE

<Kiểu cơ bản> = (phần_tử_1, phần_tử_2, ..., phần_tử_n) ;

<Tên kiểu tập hợp> = SET OF <Kiểu cơ bản> ;

VAR

<Tên biến > : <Tên kiểu tập hợp> ;

Ví dụ 8.29: TYPE

Sizes = (short, medium, large) ;

Shirtsizes = SET OF sizes ;

VAR

shortleeve, longleeve : shirtsizes ;

+ Khai báo trực tiếp

VAR

<Tên biến> : SET OF <Kiểu cơ bản> ;

Ví dụ 8.30:

VAR

Chu : SET OF Char ;

So : SET OF 0 .. 9 ;

ABC : SET OF 0 .. 256 ;

Date : SET OF (Sun, Mon, Tue, Wed, Fri, Sat) ;

2. Mô tả một tập hợp TOP

Một tập hợp được mô tả bằng cách liệt kê các phần tử của tập hợp, chúng cách nhau bằng một dấu phẩy (,) và được đặt giữa hai dấu móc vuông [ ], các phần tử có thể là hằng, biến hoặc biểu thức.

Ví dụ 8.31:

[] {tập hợp rỗng, không có các phầnt tử }

[5 .. 15] {tập hợp các chữ số nguyên từ 5 đến 15}

[1, 3, 5] {tập hợp 3 số 1, 3 và 5 }

[Hồng, Lan, Cúc, Mai] {tập hợp tên 4 loài hoa}

[i, i + j*2, 4, 5] {tập hợp các biến nguyên gồm số 4, 5 và

các số nhận từ i, i +j*2 với i, j là 2 biến nguyên}

3. Các phép toán trên tập hợp TOP

a. Phép gán

Ta có thể gán giá trị các tập đã được mô tả vào các biến tập cùng kiểu. Riêng tập hợp rỗng có thể gán cho mọi biến kiểu tập hợp khác nhau.

Với ví dụ trên, ta có thể gán :

Chu := [X,Y,Z] ;

So := [2,3,4] ;

Date := [] ;

Nếu ta viết Chu := [1,2]; thì không hợp lệ vì Chu là tập hợp các chữ.

b. Phép hợp

Hợp của 2 tập hợp A và B là một tập hợp chứa tất cả các phần tử của tập A hoặc B hoặc cả A và B.

Ký hiệu của phép hợp là dấu cộng (+). Phép hợp có tính giao hoán:

A+B = B+A

Ta có thể mô tả phép hợp qua hình ảnh sau :

Ví dụ 8.32 A := [0,1,3,5,7,9] ;

B := [0,2,4,6,8,9] ;

C := A + B ;

{tập hợp C sẽ có các phần tử là [0,1,2,3,4,5,6,7,8,9] }

c. Phép giao

Giao của 2 tập hợp A và B là một tập chứa các phần tử của cả A và cả B.

Ký hiệu A * B. Phép giao cũng có tính giao hoán, nghĩa là A * B = B * A

Minh họa như sau :

Với ví dụ trong phép hợp, nếu:

D := A * B ; {tập D chứa phần tử [0,9] }

Nếu A và B không có phần tử nào giống nhau thì phép hợp sẽ cho tập rỗng.

d. Phép hiệu

Hiệu của 2 tập hợp A và B, ký hiệu là A - B, là một tập hợp chứa các phần tử chỉ thuộc A mà không thuộc B. Lưu ý : A - B thì khác B - A.

Ví dụ 8.33: A := [3 .. 7] ;

B := [1.. 6, 10, 15] ;

thì A - B là tập hợp [7] còn B - A là tập hợp [1,2, 10,15]

e. Phép thuộc IN

Phép thuộc IN cho phép thử xem một giá trị nào đó thuộc về một tập hay không? Phép thuộc IN cho kết quả có kiểu Boolean. Nếu đúng nó sẽ cho kết quả là TRUE, ngược lại là FALSE.

Ví dụ 8.34: Chu là biến kiểu Char, còn A là biến kiểu SET OF Char và

Chu := 'X' ;

A := ['X', 'x','Y', 'y', 'Z', 'z'] ;

thì phép toán Chu IN A sẽ cho kết quả là TRUE

f. Các phép so sánh =, <>, <= và >=

Muốn so sánh 2 tập hợp với nhau thì chúng phải có cùng kiểu cơ bản. Kết quả của các phép so sánh là giá trị kiểu Boolean, tức là TRUE (Ðúng) hoặc FALSE (Sai).

Hai tập hợp A và B gọi là bằng nhau (A = B) chỉ khi chúng có các phần tử giống với nhau từng đôi một (không kế thứ tự sắp xếp các phần tử trong 2 tập). Ngược lại của phép so sánh bằng nhau (=) là phép so sánh khác nhau (<>). Nghĩa là, nếu A = B là TRUE thì A <> B sẽ là FALSE và ngược lại.

Phép so sánh nhỏ hơn hoặc bằng (<=) của A <= B sẽ cho kết quả là TRUE nếu mọi phần tử có trong A đều có trong B. Ðịnh nghĩa này cũng tương tự như lớn hơn hoặc bằng (>=). Với A >= B thì mọi phần tử của B đều có trong A, kết quả này TRUE, ngược lại là FALSE.

Chú ý: Trong Pascal không có phép so sánh nhỏ hơn (<) và lớn hơn (>). Ðể kiểm tra xem tập A có thực sự nằm trong tập B hay không (A nhỏ hơn B), ta phải sử dụng thêm các phép logic như sau:

IF (A <> B) AND (A <= B) THEN WRITELN ( 'A < B')

4. Viết và đọc dữ liệu kiểu tập hợp TOP

Với dữ liệu kiểu tập hợp, ta không thể viết ra hoặc đọc vào bằng các thủ tục (Write) Writeln hoặc (Read) Readln. Tuy nhiên, ta có thể thực hiện các thao tác này khi mà kiểu cơ bản của tập hợp là số nguyên, ký tự.

Ví dụ 8.35: Viết chương trình để đọc một câu bất kỳ, sắp xếp các chữ của câu đó theo thứ tự ABC abc từ chữ in đến chữ thường. Chương trình chấm dứt khi nhận được chữ END hoặc end.

PROGRAM Letters_used ;

TYPE letters = SET OF char ;

VAR used, unused : letters ;

count, charcount : 0 .. 80 ;

alpha : char ;

line : string ;

PROCEDURE Readinput ; {đọc một câu bất kỳ}

BEGIN

FOR count := 1 TO 80 DO line[count] := ' ' ;

Writeln (' Nhập vào một dòng câu dưới đây : ') ;

Count := 0;

WHILE NOT eoln DO {hàm eoln trả về giá trị false khi ký tự nhận vào khác}

BEGIN {ký tự kết thúc dòng CR: carry return}

count := count + 1 ;

read(line[count]);

END ;

readln;

charcount := count;

END ;

PROCEDURE Writeoutput ; {trình bày phân tích của một dòng câu }

BEGIN

writeln;

write(' Các chữ đã sử dụng: ') ;

FOR alpha := 'A' to 'z' DO

IF [alpha] <= used THEN write( ' ', alpha) ;

writeln;

writeln;

END;

BEGIN {Thân chương trình chính}

Readinput;

WHILE NOT (([line[1]] <= ['E', 'e']) AND ([line[2]] <= ['N', 'n'])

AND ([line[3]] <= ['D', 'd'])) DO

BEGIN

used := [] ;

unused := ['A' .. 'Z', 'a' .. 'z'] ;

FOR count := 1 TO charcount DO

IF [line[count]] <= unused THEN

BEGIN

used := used + [line[count]] ;

unused := unused - [line[count]];

END ;

Writeoutput ;

Readinput;

END ;

END.

Khi chạy chương trình, ta sẽ thấy (Các dòng chữ gạch dưới là của người dùng):

Nhập vào một dòng câu dưới đây:

Pascal is a structured programming language derived from ALGOL - 60

Các chữ đã sử dụng: A G L O P a c d e f g i l m n o p r s t u v

Nhập vào dòng câu dưới đây:

END

IV. KIỂU BẢN GHI (RECORD)

1. Ðịnh nghĩa và khai báo TOP

Các cấu trúc dữ liệu kiểu mảng (Array) và tập hợp (Set) có hạn chế ở chỗ các phần tử trong tập hợp của chúng phải cùng kiểu mô tả. Song trong thực tế, có những kiểu cấu trúc dữ liệu khác nhau nhưng lại có một mối liên quan nào đó.

Ví dụ 8.36:

Ðể mô tả dữ liệu về lý lịch một người nào đó, người ta phải khai báo họ tên người (kiểu String), Phái (Nam :=True, Nữ := False theo kiểu Boolean), ngày sinh (mô tả kiểu date), địa chỉ (kiểu String) và mức lương (kiểu integer), v.v... Với các kiểu cơ bản khác nhau như vậy trong Pascal, ta phải dùng kiểu bản ghi (RECORD).

Kiểu bản ghi trong ngôn ngữ Pascal gắn liền với kiểu tập tin (FILE) - sẽ được trình bày trong phần kế tiếp. Tuy nhiên, ta có thể sử dụng RECORD một cách độc lập với FILE.

RECORD là kiểu dữ liệu bao gồm nhiều thành phần có thể khác nhau về kiểu dữ liệu, mỗi thành phần được gọi là trường (Field).

Cú pháp khai báo kiểu bản ghi (Record) trước rồi khai báo biến như sau:

+ Khai báo gián tiếp:

TYPE

<Tên kiểu bản ghi> = RECORD

<Tên trường 1a>[,<Tên trường1b>,...] : <Kiểu trường> ;

<Tên trường 2a>[,<Tên trường2b>,...] : <Kiểu trường> ;

.............................................................................................. ;

END ;

VAR

<Tên biến1>[,<Tên biến2>, ...] : <Tên kiểu bản ghi> ;

Ví dụ 8.37:

Ta đang làm một khai báo về khách hàng của công ty chuyên bán hàng trả góp nào đó. Số liệu cần sử dụng là ngày tháng làm bản ghi và các thông tin cơ bản về khách hàng nợ thanh toán cho công ty, theo minh họa ở hình dưới:

Trong chương trình này, Công ty phân ra 3 tình trạng loại khách nợ (status): đúng kỳ hạn phải trả (current), đã quá hạn phải trả (overdue) và loại khách chểnh mảng, dây dưa việc trả nợ nhiều lần (delinquent). Ở đây:

- Status được khai báo theo kiểu dữ liệu liệt kê (enumerated data type).

- Account (Số kế toán) là một kiểu record, chứa các thông tin về tên và địa chỉ khách nợ (kiểu chuỗi string), số khách nợ (kiểu số nguyên integer-type), loại khách nợ (kiểu liệt kê enumerated type) và số liệu tồn đọng nợ của khách (kiểu số thực real-type).

- Date (Ngày tháng) là một kiểu Record trong chương trình ghi ngày, tháng năm đáo nợ của khách hàng.

Biến của chương trình là khách hàng (customer).

Ta có thể khai báo như sau:

TYPE status = (current, overdue, delinquent);

date = RECORD

day : 1 .. 31 ;

month : 1 ..12 ;

year : 1900 .. 2100 ;

END ;

account = RECORD

Custname : String ;

Custaddress : String ;

Custno : 1 .. 9999 ;

Custtype : status ;

Custbalance : Real ;

Lastpayment : date;

END;

VAR customer : account ;

+ Khai báo trực tiếp

VAR

<Tên biến1>[,<Tên biến2>, ...] : RECORD

<Tên trường 1a>[,<Tên trường1b>,...] : <Kiểu trường> ;

<Tên trường 2a>[,<Tên trường2b>,...] : <Kiểu trường> ;

.............................................................................................. ;

END ;

2. Truy xuất một Record TOP

Ðể truy xuất vào một trường của kiểu Record, ta cần dùng tên biến kiểu Record, sau đó là dấu chấm (.) rồi đến tên trường. Dạng tổng quát sau:

<Biến Record>.<Tên trường>

Ví dụ 8.39: Nhập lý lịch nhân viên của một cơ quan

TYPE

Lylich = RECORD {Lý lịch gồm Họ tên, Tuổi, Phái, Lương}

Hoten : string [25] ;

Tuoi : integer ;

PhaiNam : boolean; {Nam : M (Male), Nữ : F (Female)}

Luong : real;

END;

VAR x, y : Lylich ;

nv : ARRAY [1 .. 200] OF Lylich ; {nv là mảng lý lịch các nhân viên}

..................................

Write('Nhập tổng số nhân viên : '); readln(n) ;

FOR i := 1 TO n DO

BEGIN

Write(' Họ tên : '); readln(nv[i].Hoten);

Writeln(' Tuổi : '); readln(nv[i].Tuoi) ;

Write(' Phái (Nam :M, Nữ : F) ? '); readln (Phai);

IF (Phai = 'M') or (Phai ='m') THEN nv[i].PhaiNam := TRUE

ELSE nv[i].PhaiNam := FALSE ;

Writeln(' Lương : '); read(nv[i].Luong) ;

END ;

.......................................

Lưu ý :

• Các biến Record có thể gán cho nhau. Ví dụ x và y là 2 biến bản ghi có cùng kiểu Lylich, thì ta có thể gán:

x := y;

Như vậy ta không phải lặp lại:

x.Hoten := y.Hoten ;

x.Tuoi := y.Tuoi ;

.................................

• Không được viết ra màn hình hoặc đọc từ bàn phím một biến record như :

Writeln(x); hoặc Readln(x);

• Không thể so sánh các record bằng các phép toán quan hệ <, >, <=, >=,=,<>

• Không được dùng các toán số học và logic với kiểu record.

Ví dụ 8.40: Nhập vào 2 số phức C1 và C2 và tính C3 là tổng của chúng

Với chương trình loại này ta phải lần lượt nhập từng phần thực và phần ảo riêng rẽ của C1 và C2. Ta không thể dùng dòng lệnh C3 = C1 + C2. Kết quả tính C3 phải là phép cộng riêng rẽ từng phần thực và phần ảo của C1 và C2 rồi ghép lại.

PROGRAM So_Phuc ;

TYPE

Sophuc = Record

pt, pa : real ;

End;

VAR

c1, c2, c3 : Sophuc ;

BEGIN

Write('Lần lượt nhập phần thực và phần ảo của 2 số phức C1 và C2') ;

Write('Nhập phần thực của số phức C1 : ') ; Readln(c1.pt) ;

Write('Nhập phần ảo của số phức C1 : ') ; Readln(c1.pa) ;

Write('Nhập phần thực của số phức C2 : ') ; Readln(c2.pt) ;

Write('Nhập phần ảo của số phức C2: ') ; Readln(c2.pa) ;

c3.pt := c1.pt + c2.pt ;

c3.pa := c1.pa + c2.pa ;

Writeln('Kết quả của phép cộng 2 số phức :');

Write('C3 = C1 + C2 ');

Write(' = (', c1.pt:5:2, '+i ', c1.pa:5:2, ') +(', c2.pt:5:2, '+i ',c2.pa:5:2, ') ');

Write('C3 = ', c3.pt:5:2, '+i', c3.pa:5:2 );

Readln;

END.

3. Các Record lồng nhau TOP

Record lồng nhau là record mà có trường (field) của nó lại có kiểu là một record khác. Ta có thể định nghĩa các record lồng nhau theo một cách trực tiếp hay gián tiếp nhau và cách khai báo cũng hoàn toàn tương tự như cách khai báo record ở trên.

Ví dụ 8.41:

TYPE

dd_mm_yy = Record

dd:1..31;

mm:1..12;

yyyy:1900..2100;

end;

hoso = Record

masv:string[7];

ngsinh:dd_mm_yyyy;

diem:real;

end;

VAR

Lop: Array[1..20] of hoso;

4. Câu lệnh WITH TOP

Nhiều chương trình đòi hỏi các phần tử khác nhau của cùng một record phải được thao tác tại các vị trí khác nhau bên trong chương trình. Như vậy phải cần có nhiều chỉ thị trường khác nhau đặc trưng. Việc này làm chương trình trở nên phức tạp, tẻ nhạt và khó theo dõi. Ðể giải quyết tình trạng này, Pascal đã đưa ra cấu trúc câu lệnh WITH ... DO trong record nhằm bớt đi các rắc rối từ các chỉ thị trường (hay nói cách khác, câu lệnh WITH .. DO như là phép toán đặt thừa số chung mà ở đó thừa số chung là các tên biến record).

Dạng tổng quát của câu lệnh WITH là:

WITH <tên biến record> DO

BEGIN

<Câu lệnh với tên trường 1>;

............................................. ;

<Câu lệnh với tên trường n>;

END ;

Ví dụ 8.42:

Một biến bản ghi DANSO có các trường KHUVUC, HOTEN, NGAYSINH, DIACHI, NGHE đưa dữ liệu từ bàn phím như sau:

WITH DANSO DO

BEGIN

Write ('Khu vực điều tra :'); Readln (KHUVUC);

Write ('Họ tên công dân :'); Readln (HOTEN);

Write ('Ngày-tháng-năm sinh :'); Readln (NGAYSINH);

Write ('Ðịa chỉ công dân :'); Readln (DIACHI);

Write ('Nghề nghiệp : '); Readln (NGHE);

END ;

Ðối với bản ghi có nhiều thứ bậc:

R1 là biến bản ghi có trường R2

R2 là biến bản ghi con có trường R3

R3 là biến bản ghi con có trường R4

.....................

thì câu lệnh WITH được tổ chức lồng nhau:

WITH R1 DO

WITH R2 DO

...............

WITH RN DO <Câu lệnh ...> ;

hay viết đơn giản hơn:

WITH Record1, Record2, ..., RecordN DO <Câu lệnh BEGIN .. END> ;

Ở ví dụ 8.41, ta có thể viết như sau:

WITH Lop[i] DO

Begin

With Ngsinh Do

Begin dd:= 25; mm:=05; yyyy:=1978; End;

masv:='7962343'; diem:=9.0;

End;

_______________________________________________________________________________________________

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

V. KIỂU TẬP TIN (FILE).

1. Giới thiệu.

2. Tập tin định kiểu.

3. Tập tin văn bản.

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

Cấu trúc của bản ghi là các trường với tên, kiểu dữ liệu và độ rộng. Các kiểu record nêu ở trên là kiểu bản ghi cố định, không thay đổi. Trong thực tế, ta thường gặp một số bản ghi có đến hàng chục trường, mà trong đó chỉ có một số trường là mọi đối tượng đều có (phần cố định), còn lại một số trường khác thì mỗi đối tượng lại có phần khác nhau (phần thay đổi). Việc tổ chức một bản ghi cố định theo cách đã học ở trên sẽ gây lãng phí bộ nhớ. Ngôn ngữ Pascal cho phép thành lập các bản ghi có một phần cấu trúc thay đổi được hay còn gọi là record thay đổi.

Chẳng hạn, để ghi kết quả môn thi của sinh viên, trong đó 3 ngành:

- Nông nghiệp: Trồng trọt, Chăn nuôi, Thủy sản, Chế biến, Anh văn, Tin học

- Sư phạm: Toán học, Vật lý, Hoá học, Sinh học, Anh văn, Tin học

- Công nghệ: Xây dựng, Cơ khí, Thủy lợi, Môi trường, Anh văn, Tin học

Như vậy, trong 3 ngành trên đều có chung môn Anh văn và Tin học (phần cố định), còn lại là mỗi ngành lại có môn học phân biệt khác nhau (phần thay đổi). Thay vì khai báo 18 môn học cho 3 ngành khác nhau, ta có thể tổ chức cách khác bằng cách tạo ra một cấu trúc record thay đổi. Trước hết, ta xét các định nghĩa trong một bản ghi có cấu trúc thay đổi:

• Phần cố định (các trường cố định) là phần giống nhau trong mọi trường hợp của tất cả mọi đối tượng. Trong record có cấu trúc thay đổi, phần này có thể có hoặc không, nếu có thì phần cố định sẽ được khai báo trước. Phần cố định được viết bình thường như đã trình bày ở phần trên.

• Trong các trường cố định, ta chọn một trường làm chỉ tiêu để phân loại cho các trường hợp mà mỗi trường hợp có một số trường khác nhau (trường thay đổi) tương ứng với từng giá trị mà trường làm chỉ tiêu phân loại nhận. Trường làm chỉ tiêu phân loại này được gọi là trường phân loại, như vậy trường phân loại này cũng chính là trường cố định. Trường phân loại này luôn được đặt sau cùng trong các trường cố định.

• Phần thay đổi (các trường thay đổi ) luôn được đặt sau trường phân loại. Trường phân loại này được đặt trong từ khóa CASE .. OF. Tùy theo trường phân loại này nhận giá trị nào thì sẽ có các trường thay đổi tương ứng.

Dạng tổng quát của record thay đổi sau:

TYPE

<tên kiểu> = <kiểu trường> ;

..........................

<tên kiểu record> = RECORD

<Tên trường 1a>[,<Tên trường1b>,...] : <Tên kiểu> ;

<Tên trường 2a>[,<Tên trường2b>,...] : <Tên kiểu> ;

........................... {các trường cố định}................................ ;

CASE <tên trường phân loại> : <tên kiểu> OF

<giá trị 1> : (<danh sách các trường thay đổi 1>:< Tên kiểu >);

<giá trị 2> : (<danh sách các trường thay đổi 2>:< Tên kiểu >);

...................................{Các trường thay đổi}....................................... ;

END;

Ghi chú: Danh sách của trường thay đổi tùy thuộc vào từng giá trị cụ thể của trường phân loại, dấu ngoặc đơn ( ... ) bao danh sách của trường thay đổi là bắt buộc phải có kể cả khi nó rỗng.

Với ví dụ trên ta thấy thay vì ghi một bản ghi 18 môn học, ta chỉ cần dùng record biến đổi có số lượng môn học là 6 theo thực tế của mỗi sinh viên.

Ví dụ 8.43: Trong ví dụ trên, bài toán lập trình có thể viết như sau:

TYPE

Diemthi= REAL;

NGANH = (NN, SP, CN) ;

DSMONHOC = RECORD

AVAN, TINHOC: Diemthi;

CASE MONHOC : NGANH OF

NN : (TTROT, CNUOI, TSAN, CBIEN: Diemthi) ;

SP : (TOAN, LY, HOA, SINH: Diemthi) ;

CN : (XDUNG, CKHI, TLOI, MTRUONG: Diemthi) ;

END ;

Ở bản ghi DSMONHOC, ta có 2 trường cố định là AVAN và TINHOC, một trường phân loại là MONHOC, và 4 trường thay đổi tương ứng tùy theo trường phân loại MONHOC nhận gía trị là NN, SP, hay CN.

Ví dụ 8.44: Xét một lớp các loại hình phẳng là gồm các cá thể : tam giác, chữ nhật, và hình tròn. Mỗi loại hình đều có :

* Các chỉ tiêu chung là: Tên hình, diện tích, chu vi.

* Các chỉ tiêu riêng là:

- Ba cạnh đối với tam giác.

- Chiều dài, chiều rộng đối với hình chữ nhật.

- Bán kính đối với hình tròn.

Ta dùng kiểu bản ghi có cấu trúc thay đổi, để viết chương trình tính tính diện tích và chu vi cho từng loại hình như sau:

PROGRAM BAN_GHI_THAY_DOI;

USES CRT;

TYPE Cathe = RECORD

Dt,cv:real;

Case kieu:(TG,CN,HT) of

TG: (c1,c2,c3:real);

CN: (cr,cd:real);

HT: (bk:real);

End;

VAR

Doituong: Cathe;

Tenhinh:string[2];

i:byte;ds:boolean;

BEGIN

Clrscr;

Repeat

Write('Nhập loại hình "TG/CN/HT" vào: ');readln(tenhinh);

For i:=1 to length(tenhinh) do tenhinh[i]:=upcase(tenhinh[i]);

Until (tenhinh='TG') OR (tenhinh='CN') OR (tenhinh='HT');

If tenhinh='TG' then doituong.kieu:=TG;

If tenhinh='CN' then doituong.kieu:=CN;

If tenhinh='HT' then doituong.kieu:=HT;

With doituong do

Case kieu of

TG: begin

Writeln('ÐỐI TƯỢNG LÀ HÌNH TAM GIÁC');

Write(' Nhập cạnh thứ nhất = ');readln(c1);

Write(' Nhập cạnh thứ hai = ');readln(c2);

Write(' Nhập cạnh thứ ba = ');readln(c3);

If (c1+c2>c3) AND (c1+c3>c2) and (c2+c3>c1) then

Begin

Cv:=(c1+c2+c3)/2; {nửa chu vi}

Dt:=SQRT(cv*(cv-c1)*(cv-c2)*(cv-c3));

Cv:=cv*2; {chu vi};

Writeln(' Chu vi = ',cv:6:2);

Writeln(' Diện tích = ',dt:6:2);

End

Else writeln(' Không thỏa tính chất tam giác');

End;

CN: begin

Writeln('ÐỐI TƯỢNG LÀ HÌNH CHỮ NHẬT');

Write('Nhập chiều rộng = ');readln(cr);

Write('Nhập chiều dài = ');readln(cd);

Dt:=cr*cd;

Cv:=(cd+cr)*2;

Writeln(' Chu vi = ',cv:6:2);

Writeln(' Diện tích = ',dt:6:2);

End;

HT: begin

Writeln(' ÐỐI TƯỢNG LÀ HÌNH TRÒN');

Write('Nhập bán kính = ');readln(bk);

Cv:=2*Pi*bk; dt:=pi*SQR(bk);

Writeln(' Chu vi = ',cv:6:2);

Writeln(' Diện tích = ',dt:6:2);

End;

End;

Readln;

END.

Ví dụ 8.45: (Xem xét kỹ hơn ở trường hợp ví dụ 8.37)

Một Công ty bán hàng trả góp cần lập trình một Hệ thống Hóa đơn Khách hàng (Customer Billing System). Chương trình này đòi hỏi tạo một record về khách hàng vào máy tính. Mỗi khách hàng sẽ được cân đối số tiền nợ tồn hàng ngày dựa vào các số tiền chi trả trước và mức phải chi trả hiện hành.

Mỗi tài khoản sẽ được xem xét là đúng kỳ hạn, trừ khi :

- Mức chi trả hiện hành là lớn hơn 0 nhưng nhỏ hơn 10% của cân đối nợ tồn lần trước. Trong trường hợp này, tài khoản sẽ được xem như quá hạn (overdue).

- Nếu cân đối tồn nợ vẫn còn và giá trị chi trả hiện hành là 0, trong trường hợp này tài khoản sẽ được xem xét như phạm lỗi chểnh mảng, dây dưa" việc trả nợ (delinquent).

Ðể dễ dàng thao dõi, tất cả các bản ghi (record) sẽ được trữ như các phần tử của một mảng một chiều. Do vậy, chiến lược lập trình tổng thể sẽ như sau:

(1). Khai báo số lượng tài khoản (chính là số record) phải xử lý.

(2). Trong mỗi bản ghi, đọc các hạng mục sau:

a. Tên khách hàng (name)

b. Số nhà và tên đường (street)

c. Tên thành phố (city)

d. Số tài khoản (account number)

e. Nợ trước (previous balance)

f. Chi trả hiện hành (current payment)

g. Ngày chi trả (payment date)

(3). Sau khi tất cả các bản ghi được nhập vào máy tính, xử lý mỗi bản ghi theo cách sau:

a. So sánh chi trả hiện hành với số nợ tồn và định tình trạng nợ phù hợp.

b. Tính toán cân đối tài khoản mới bằng cách lấy nợ trước trừ đi số chi trả hiện hành (nếu ra số âm thì xem đó là một tín dụng - credit).

(4). Sau khi mỗi bản ghi đã được xử lý, hiện hình các thông tin sau cho mỗi bản:

a. Tên khách hàng (name)

b. Số tài khoản (account number)

c. Tên số nhà và tên đường (street)

d. Tên thành phố (city)

e. Cân nợ cũ (old balance)

f. Chi trả hiện hành (current payment)

g. Cân nợ mới (new balance)

h. Tình trạng tài khoản (account status)

Chúng ta sẽ lập trình theo cách thức module, với một thủ tục (procedure) riêng biệt để nhập dữ liệu, thủ tục xử lý dữ liệu và thủ tục in ra màn hình kết quả. Mỗi thủ tục sẽ viết theo lối thẳng tiến và không cần phải mô tả tỉ mỉ. Thân chương trình chính sẽ làm đơn giản nhập các dữ liệu record và sau đó truy xuất các thủ tục thích hợp. Các chữ viết tắt theo giải thích ở ví dụ 8.37. Sau đây là một trình Pascal về bài toán trên (viết theo Byron S. Gottried, xem sách tham khảo):

PROGRAM customers ;

{Chương trình HỆ THỐNG HÓA ÐƠN KHÁCH HÀNG }

{dùng minh họa cách sử dụng Record}

TYPE status = (current, overdue, delinquent) ;

Date = RECORD

day : 1 .. 31;

month : 1 .. 12;

year : 1900 .. 2100;

END;

Account = RECORD

name : string ;

street : string ;

city : string ;

custno : 1 .. 9999 ;

custtype : status ;

oldbalance : real ;

newbalance : real ;

payment : real ;

paydate : date ;

END ;

VAR Customer : ARRAY [1 .. 10] OF account ;

i, n : 1 .. 10 ;

PROCEDURE Readinput ;

{Ðọc số liệu nhập cho mỗi bản ghi}

VAR space : char ;

BEGIN

FOR i := 1 TO n DO

WITH customer[i] DO

BEGIN

Writeln ('Khách hàng số :' , i:3) ;

Write(' Tên khách hàng : ');Readln(name) ;

Write (' Số nhà và tên đường : ') ;Readln (street) ;

Write ( 'Tên thành phố :' ) ;Readln (city) ;

Write (' Số tài khoản : ') ;Readln (custno) ;

Write ( 'Cân nợ trước : ') ;Readln (oldbalance) ;

Write ( 'Chi trả hiện hành : ') ;Readln (payment) ;

Write ( 'Ngày trả (dd mm yyyy) : ') ;

WITH paydate DO

Readln(day,month, year) ;

END; {readinput}

PROCEDURE Processdata ;

{Xác định tình trạng trả nợ và tính cân nợ mới cho mỗi bản ghi}

BEGIN

FOR i := 1 TO n DO

WITH customer[i] DO

BEGIN

custtype := current ;

IF (payment > 0) AND (payment < 0.1* oldbalance)

THEN custtype := overdue ;

IF (oldbalance > 0) AND (payment = 0)

THEN custtype := delinquent ;

newbalance := oldbalance - payment

END ;

END ; {processdata}

PROCEDURE Writeoutput;

{ In ra các thông tin hiện hành cho mỗi bản ghi }

BEGIN

FOR i := 1 TO n DO

WITH customer[i] DO

BEGIN

Writeln ;

Write(' Tên khách hàng :' , name) ;

Writeln(' Số tài khoản : ' , custno) ;

Writeln( 'Số nhà và tên đường :' , street) ;

Writeln(' Tên thành phố :' , city) ;

Writeln ;

Write(' Tồn nợ cũ :' , oldbalance :7:2) ;

Write(' Chi trả hiện hành :' , payment :7:2) ;

Writeln(' Cân đối mới :' , newbalance :7:2) ;

Writeln ;

Write(' Tình trạng tài khoản : ' ) ;

CASE custtype OF

current : writeln(' CURRENT ') ;

overdue : writeln(' OVERDUE ') ;

delinquent : writeln(' DELINQUENT ') ;

END ;

Writeln;

END ;

END ; {Writeoutput}

BEGIN {Thân chương trình chính}

Writeln(' HỆ THỐNG HÓA ÐƠN KHÁCH HÀNG ') ;

Writeln ;

Writeln(' Có bao nhiêu khách hàng ? ') ;

Readln (n) ;

Readinput ;

Processdata ;

Writeoutput ;

END.

Giả sử chúng ta có số liệu 4 khách hàng nào đó. Nhập dữ liệu như bên dưới, lưu ý là các chữ số có gạch dưới là của người sử dụng nhập vào máy tính.

HỆ THỐNG HÓA ÐƠN KHÁCH HÀNG

Có bao nhiêu khách hàng ? 4

Khách hàng số 1

Tên khách hàng : Ngô Hoàng Trọng

Số nhà và tên đường : 123 Trần Hưng Ðạo

Tên thành phố : Cần thơ

Tài khoản số : 4208

Cân nợ trước : 247.88

Chi trả hiện hành : 25.00

Ngày trả (dd mm yyyy) : 14 6 1993

Khách hàng số 2

Tên khách hàng : Huỳnh Văn Tuấn

Số nhà và tên đường : 151/53 Lộ Tầm vu

Tên thành phố : Cần thơ

Tài khoản số : 2219

Cân nợ trước : 135.00

Chi trả hiện hành : 135.00

Ngày trả (dd mm yyyy) : 10 8 1993

Khách hàng số 3

Tên khách hàng : Lê Tấn Sĩ

Số nhà và tên đường : 71 Phan Ðình Phùng

Tên thành phố : Long xuyên

Tài khoản số : 8452

Cân nợ trước : 387.42

Chi trả hiện hành : 35.00

Ngày trả (dd mm yyyy) : 4 7 1993

Khách hàng số 4

Tên khách hàng : Tạ Quang Trinh

Số nhà và tên đường : 82 Mậu thân

Tên thành phố : Rạch giá

Tài khoản số : 711

Cân nợ trước : 260.00

Chi trả hiện hành : 0

Ngày trả (dd mm yyyy) : 27 11 1993

Chương trình khi chạy sẽ in ra kết quả tương ứng với số liệu nhập trên như sau:

Tên khách hàng : Ngô Hoàng Trọng Số tài khoản : 4208

Số nhà và tên đường : 123 Trần Hưng Ðạo

Tên thành phố : Cần thơ

Tồn nợ cũ : 247.88 Chi trả hiện hành : 25.00 Cân đối mới : 222.88

Tình trạng tài khoản : CURRENT

Tên khách hàng : Huỳnh Văn Tuấn Số tài khoản : 2219

Số nhà và tên đường : 151/53 Lộ Tầm vu

Tên thành phố : Cần thơ

Tồn nợ cũ : 135.00 Chi trả hiện hành : 135.00 Cân đối mới : 0.00

Tình trạng tài khoản : CURRENT

Tên khách hàng : Lê Tấn Sĩ Số tài khoản : 8452

Số nhà và tên đường : 71 Phan Ðình Phùng

Tên thành phố : Long xuyên

Tồn nợ cũ : 387.42 Chi trả hiện hành : 35.00 Cân đối mới : 352.42

Tình trạng tài khoản : OVERDUE

Tên khách hàng : Tạ Quang Trinh Số tài khoản : 711

Số nhà và tên đường : 82 Mậu thân

Tên thành phố :Rạch giá

Tồn nợ cũ : 260.00 Chi trả hiện hành : 0.00 Cân đối mới : 260.00

Tình trạng tài khoản : DELINQUENT

Sinh viên có thể thử lại và tự cải tiến chương trình này như một bài tập.

V. KIỂU TẬP TIN (FILE)

1. Giới thiệu TOP

Bây giờ chúng ta chuyển sang một kiểu cấu trúc dữ liệu quan trọng khác là kiểu tập tin hay kiểu file (tiếng Anh) hoặc Fichier (tiếng Pháp). Một số tên gọi cho kiểu file khác là kiểu tệp hay kiểu hồ sơ. Một tập tin (file) có thể được xem như một loạt các bản ghi (record) có cùng một kiểu cấu trúc, hoặc có thể xem một tập tin như những dòng thông tin không cấu trúc, chẳng hạn như những hàng chữ. Các kiểu dữ liệu khác như kiểu mảng, kiểu tập hợp và kiểu bản ghi chỉ tổ chức được trên bộ nhớ trong (RAM) của máy tính nên khi chạy xong chương trình thì dữ liệu cũng mất luôn. Ðiều này dễ gây khó chịu cho người sử dụng mỗi khi chạy chương trình phải nhập lại. Không giống như các kiểu cấu trúc dữ liệu nói trên, dữ liệu kiểu files có thể được lưu trữ trong các nguồn phụ trợ như đĩa mềm, đĩa cứng hoặc băng từ. Như vậy, cấu trúc file cho phép chúng ta lưu trữ thông tin thường xuyên và có thể truy xuất thông tin bất kỳ lúc nào cần thiết. Nhiều ứng dụng quan trọng rất cần khả năng này.

Pascal truy xuất tập tin theo 3 dạng sau:

* Tập tin định kiểu (Typed File)

* Tập tin văn bản (Text File)

* Tập tin không định kiểu (UnTyped File)

Trong bài này, ta xét hai dạng tập tin định kiểu và tập tin văn bản của Pascal.

2. Tập tin định kiểu TOP

Tập tin định kiểu là tập tin mà các phần tử (mẫu tin) của nó có cùng độ dài.

Kiểu tập tin có thể là kiểu cơ sở như ký tự, nguyên, thực,... hoặc kiểu có cấu trúc như mảng, bản ghi,...

a. Cách khai báo

+ Khai báo gián tiếp

TYPE

<Kiểu File> = FILE OF <Kiểu phần tử> ;

VAR

<Biến File> : <Kiểu File> ;

Ví dụ 8.46:

TYPE {Ðịnh nghĩa các kiểu tập tin}

FileNguyen = FILE OF integer ;

FileReal = FILE OF real ;

FileKyTu = FILE OF char ;

NhanSu = RECORD

HoTen : string[25] ;

Tuoi : Byte ;

Dchi : string[35] ;

Luong : real ;

END ;

FileNhanSu = FILE OF NhanSu ;

VAR

FN : FileNguyen ;

FR : FileReal ;

FC : FileKyTu ;

FP : FileNhanSu ;

+ Khai báo trực tiếp

VAR

<Biến File> : FILE OF <Kiểu phần tử> ;

Ví dụ 8.47:

TYPE

NhanSu = RECORD

HoTen : string[25] ;

Tuoi : Byte ;

Dchi : string[35] ;

Luong : real ;

END ;

VAR

FN : FILE OF integer ;

FR : FILE OF real ;

FC : FILE OF char;

FP : FILE OF NhanSu ;

Trong các ví dụ trên, ta có:

- Biến tập tin FN là một tập tin kiểu nguyên với mỗi phần tử của chúng là một số nguyên và có độ dài là 2 bytes.

- Biến tập tin FR là một tập tin kiểu thực với mỗi phần tử của chúng là một số thực và có độ dài là 6 bytes.

- Biến tập tin FC là một tập tin kiểu ký tự với mỗi phần tử của chúng là một ký tự và có độ dài là 1 byte.

- Biến tập tin FP là một tập tin kiểu bản ghi với mỗi phần tử của chúng là một bản ghi và có độ dài là 67 bytes.

b. Truy xuất các phần tử của tập tin

Tổng quát, có 2 loại tập tin có thể tạo ra và truy xuất bởi một chương trình máy tính: tập tin thường xuyên (permanent file) và tập tin tạm thời (temporary file). Các tập tin thường xuyên được trữ trong các thiết bị bộ nhớ phụ trợ và do vậy được duy trì sau khi chương trình hoàn tất việc thực hiện. Các phần tử của tập tin thường xuyên( là các thành phần tập tin riêng rẽ) có thể được truy xuất/ cải biên từ chương trình tạo ra nó hoặc từ một số chương trình khác bất kỳ lúc nào. Trong Pascal, tập tin thường xuyên được xem như các tập tin ngoại trú (external files).

Mặt khác, các tập tin tạm thời được trữ trong bộ nhớ chính của máy tính. Tập tin tạm thời sẽ bị mất đi khi chương trình đã được thi hành xong. Như vậy, tập tin tạm thời ít hữu dụng hơn tập tin thường xuyên. Trong Pascal, tập tin tạm thời được xem như các tập tin nội trú (internal file).

Tất cả các tập tin, dù là thường xuyên hay tạm thời, đều có thể tổ chức thành một trong 2 cấu trúc khác nhau: tuần tự (sequential) hoặc ngẫu nhiên (random). Muốn truy xuất các phần tử của tập tin ta phải có một biến đệm (tampon variable) gọi là con trỏ của tập tin. Con trỏ có thể di chuyển trong toàn bộ tập tin. Trong một tập tin có cấu trúc tuần tự, tất cả các phần tử trong tập tin được lưu trữ một cách tuần tự, phần tử này theo sau phần tử kia. Ðể có thể truy xuất một phần tử đặc thù nào đó thì cần thiết phải di chuyển con trỏ đi từ điểm khởi đầu và tìm qua tập tin toàn bộ cho đến khi phần tử mong muốn được tìm ra. Kiểu truy xuất này rất chậm nếu ta gặp một tập tin khá lớn. Tập tin tuần tự tương đối dễ tạo ra, tuy nhiên, chúng chỉ là một loại tập tin thích hợp có thể sử dụng với một kiểu phương tiện lưu trữ nào đó (ví dụ như băng từ).

Trong một tập tin truy xuất ngẫu nhiên, bất kỳ phần tử nào cũng có thể truy xuất trực tiếp, mà không phải qua tiến trình qua tập tin toàn bộ từ điểm khởi đầu. (Tập tin ngẫu nhiên cũng được biết như các tập tin truy xuất trực tiếp - direct access file, thực tế nó chỉ là cái tên mô tả). Các tập tin kiểu như vậy thì truy xuất nhanh hơn các tập tin tuần tự mặc dù nó khó tạo ra và duy trì.

Pascal chuẩn (ANSI Pascal) chỉ xác lập loại tập tin tuần tự, trong khi một số ngôn ngữ thực hành khác, bao gồm cả Turbo Pascal có hỗ trợ việc dùng tập tin truy cập trực tiếp.

c. Các thủ tục và hàm trên tập tin

Gọi:

- FileVar là 1 biến tập tin và ,

- FileName là một biểu thức kiểu string.

Truy xuất một biến tập tin, cần thông qua các thủ tục và hàm sau:

* Thủ tục ASSIGN(FileVar, FileName)

Lệnh này dùng để gán tên tập tin FileName cho 1 biến file là FileVar. Filename là tên của một tập tin, do vậy FileName cần tuân theo cách đặt tên của MS-DOS.

Ví dụ 8.48:

VAR Fnguyen : FILE OF integer ;

BEGIN

ASSIGN(Fnguyen, 'SONGUYEN.DAT ') ;

{ Gán tập tin có tên là SONGUYEN cho biến Fnguyen }

REWRITE(FileVar) {Cậu lệnh sẽ giải thích ở phần kế}

................

* Thủ tục REWRITE (FileVar)

Lệnh này mở một tập tin mới trên đĩa từ có tên là FileName (đã khai báo trong thủ tục ASSIGN). Nếu FileName đã có sẵn trong đĩa thì nội dung của nó bị xóa và con trỏ đặt ở đầu tập tin. Trong cả 2 trường hợp, sau lệnh này trong tập tin đều rỗng và thông tin mới sẽ được đưa vào nhờ thủ tục WRITE sẽ trình bày ở phần sau.

* Thủ tục RESET(FileVar)

Mở tập tin có sẵn trong đĩa từ có tên là FileName đã khai báo trong lệnh ASSIGN, con trỏ tập tin đặt ở đầu tập tin, dùng lệnh READ để đọc thông tin (sẽ đề cập ở phần sau).

* Thủ tục WRITE(FileVar, x1, x2, ..., xn)

Lệnh này ghi lần lượt các biến x1, x2, ..., xn, là các biến thuộc kiểu phần tử của biến File, vào tập tin có tên là FileName (trong lệnh ASSIGN) vào đĩa từ theo ví trí tuần tự của con trỏ đang đứng.

* Thủ tục READ(FileVar, x1, x2, ..., xn)

Lệnh này đọc tuần tự các giá trị x1, x2, ..., xn tại vị trí con trỏ đang đứng rồi gán vào các biến tương ứng cùng kiểu.

Chú ý: Sau khi đọc hoặc ghi một phần tử, thì con trỏ tập tin sẽ di chuyển xuống vị trí phần tử tiếp theo.

Ví dụ 8.49:

Program Write_Read;

Var Tepnguyen: File Of integer;

i: integer;

Begin

Assign(tepnguyen,'U:\SONGUYEN.DAT ');

Rewrite(Tepnguyen);

For i :=1 to 4 do write(tepnguyen,i);

Read(tepnguyen,i);

write('i= ',i:4);

end;

Close(Tepnguyen);

Readln(tepnguyen);

End.

* Thủ tục SEEK(FileVar, n)

Lệnh này chỉ thị con trỏ tập tin chuyển đến vị trí phần tử thứ n của tập tin. Trong đó, n là số nguyên chỉ vị trí các phần tử trong tập tin, phần tử thứ nhất có số thứ tự là 0.

* Hàm FILEPOS(FileVar):integer

Cho kết quả là một số nguyên chỉ vị trí hiện tại của con trỏ tập tin.

* Hàm FILESIZE (FileVar):integer

Cho kết quả là một số nguyên chỉ số lượng phần tử có trong tập tin. FileSize nhận giá trị 0 khi tập tin rỗng, không có phần tử nào. Ðể đưa con trỏ về cuối tập tin, ta dùng thủ thuật:

SEEK(FileVar, FileSize(FileVar)) ;

* Thủ tục FLUSH(FileVar)

Cho phép đẩy thông tin nằm trong vùng nhớ đệm vào ra của tập tin ra đĩa từ.

* Thủ tục CLOSE(FileVar)

Ðóng tập tin lại và cập nhật thư mục để phản ánh tình trạng mới của tập tin.

* Thủ tục ERASE(FileVar)

Xóa tập tin trên đĩa. Chú ý tập tin phải có thủ tục CLOSE trước khi xóa.

* Thủ tục RENAME(FileVar, NewName)

Ðổi tên tập tin FileName thành NewName. NewName phải có qui cách của một tên tập tin.

* Hàm EOF(FileVar):Boolean

Hàm EOF (End-Of-File) này dùng để kiểm tra tình trạng hết tập tin. Nếu con trỏ ở cuối file thì nó sẽ cho kết quả là TRUE, ngược lại con trỏ ở bất kỳ nơi khác không phải ở cuối tập tin là FALSE.

Mỗi ô là một phần tử của tập tin. Nếu con trỏ nằm ở vị trí cuối, EOF sẽ là TRUE.

d. Kiểm tra các tập tin khi mở

Khi làm việc trên tập tin, ta có thể gặp một số rắc rối như:

• Khi dùng thủ tục RESET để mở một file nào đó thì ta có thể không rõ liệu tập tin đã có chưa? Nếu chưa thì chương trình sẽ báo File not found và ngưng thực hiện tiếp các lệnh về sau.

• Khi dùng thủ tục REWRITE để mở một file mới thì có thể tập tin cũ bị xóa toàn bộ nếu nó đã có trên đĩa.

• Ngoài ra ta cũng cần biết đĩa còn đủ chỗ để chứa thêm thông tin mới của tập tin không?

Turbo Pascal có cung cấp các chỉ dẫn giúp tránh một số vấn đề trên qua chương trình dịch đóng/mở việc kiểm tra sai lỗi trong quá trình vào/ra tập tin:

{$I+} : Mở kiểm tra, khi có lỗi vào/ra thì chương trình sẽ báo lỗi và ngưng thực hiện. Ðây là chế độ ngầm định (by default), nghĩa là chương trình dịch luôn luôn thực hiện chế độ nếu không được báo rõ.

{$I-} : Tắt chế độ kiểm tra lỗi vào/ra, khi có lỗi chương trình không ngừng lại nhưng sẽ treo các thủ tục vào/ra khác cho đến khi có lời gọi hàm kết quả IOResult.

Hàm IOResult: Word là hàm kiểm tra lỗi vào/ra và cho kết quả là một số nguyên kiểu word. Hàm này thường dùng để kiểm tra xem tập tin có tồn tại trên đĩa hay không? Khi có lời gọi hàm IOResult thì điều kiện sai bị xoá bỏ và các thủ tục vào ra vẫn tiếp tục. Nhiệm vụ sửa lỗi sẽ thuộc về người lập trình. Nếu IOResul = 0 thì file đã có trên đĩa, ngược lại thì file không có.

Ví dụ 8.50: Một chương trình mẫu để kiểm tra file khi đọc.

PROGRAM KiemtraIOFile ;

VAR Loi : Boolean ;

BEGIN

Repeat

Write(Tên tập tin : ) ;

Readln(Filename) ;

Assign(F, FileName) ;

{$I-} {giao việc kiểm lỗi cho người dùng}

RESET(F) ;

Loi := IOResult = 0 ;

{$I+}

IF NOT Loi THEN Write( Không mở tập tin này được ! ) ;

Until Loi ;

END.

Ví dụ 8.51: { Về tập tin có kiểu bản ghi }

Trở lại với ví dụ 8.45 về Hệ thống Hóa đơn Khách hàng. Các bản ghi cho mỗi khách hàng cần phải được cập nhật (update) từ file cũ và ghi lại thành một file mới. Tùy theo điều kiện trả nợ của mỗi khách hàng mà ta sẽ thay đổi tình trạng nợ nần của họ. Các bước tiến hành cho việc lập chương trình như sau:

(1) Ðọc các bản ghi từ tập tin cũ (Old file)

(2) Thể hiện tên khách hàng, số tài khoản, chi trả kỳ trước... ra màn hình. Cho dấu nhắc con trỏ ở vị trí Chi trả hiện hành (Current Payment).

(3) Cập nhật bản ghi bằng cách nhập vào số tiền chi trả và ngày chi trả.

(4) Xác định cân đối nợ mới (New balance) và tình trạng tài khoản hiện hành (Current account status) và thể hiện ra màn hình.

(5) Ghi bản ghi thành tập tin dữ liệu mới (New data file) bất chấp có hay không việc cập nhật bản ghi.

(6) Tiếp tục tiến trình này cho đến khi tất cả các bản ghi trong tập tin cũ đã được xử lý xong.

Dưới đây là chương trình Pascal theo Byron S. Gottfried:

PROGRAM Billing; {Cập nhật bản ghi khách hàng và tạo ra một tập tin mới }

TYPE date = RECORD

day : 1 .. 31;

month : 1 .. 12 ;

year : 1900 .. 2100;

END;

account = RECORD

name : string ;

cusno : 1 .. 9999 ;

oldbalance : real ;

newbalance : real ;

payment : real ;

paydate : date ;

END ;

datafile = FILE OF account ;

VAR newfile, oldfile : datafile ;

newaccount, oldaccount : account ;

count : 1 .. 9999 ;

space : char ;

PROCEDURE update (VAR custaccount : account) ;

{Cập nhật bản ghi}

BEGIN

WITH custaccount DO

BEGIN

Writeln (' Khách hàng số :' , count) ;

Write(' Tên khách hàng :' , name);

Writeln (' Số tài khoản :' , custno:4) ;

Write (' Cân nợ trước :' , oldbalance :7:2) ;

payment := 0

Write (' Chi trả hiện hành : ') ; Readln (payment) ;

newbalance := oldbalance - payment;

IF payment > 0 THEN

BEGIN

Write (' Ngày trả (dd mm yyyy) : ') ;

WITH paydate DO

Readln (day, space, month, space, year) ;

END;

Writeln(' Cân nợ mới :' , newbalance :1:2) ;

If payment > 0 then oldbalance := newbalance ;

Write(' Tình trạng tài khoản : ') ;

IF payment >= 0.1* oldbalance

THEN Writeln( 'CURRENT')

ELSE IF payment > 0 THEN Writeln ( 'OVERDUE')

ELSE Writeln ( 'DELINQUENT") ;

END; {WITH custaccount}

END;

BEGIN {Thân chương trình chính}

ASSIGN(oldfile, 'old.dat') ;

ASSIGN(newfile, 'new.dat') ;

RESET(oldfile) ;

REWRITE(newfile) ;

count := 1 ;

space := ' ' ;

Writeln(' HỆ THỐNG HÓA ÐƠN KHÁCH HÀNG - CẬP NHẬT FILE ');

Writeln;

BEGIN {cập nhật tập tin cũ}

WHILE NOT eof(oldfile) DO

BEGIN

Read(oldfile, oldaccount) ;

update(oldaccount) ;

newaccount := oldaccount ;

write(newfile, newaccount) ;

count := count + 1

END; {WHILE NOT eof(oldfile) }

END; {file update}

Close(oldfile) ; close(newfile) ;

END.

Chú ý chương trình đọc tập tin cũ với tên ngoại trú là old.dat và tạo ra một tập tin mới với tên ngoại trú là new.dat.

Giả sử chúng ta có số liệu 4 khách hàng như ví dụ 8.43ï. Kết quả hội thoại giao tiếp bởi tiến trình cập nhật được thể hiện như sau. Lưu ý là các chữ số có gạch dưới là của người sử dụng nhập vào máy tính.

HỆ THỐNG HÓA ÐƠN KHÁCH HÀNG - CẬP NHẬT FILE

Khách hàng số 1

Tên khách hàng : Ngô Hoàng Trọng Tài khoản số : 4208

Cân nợ trước : 247.88

Chi trả hiện hành : 25.00

Ngày trả (dd mm yyyy) : 14 6 1993

Cân nợ mới : 222.88

Tình trạng tài khoản : CURRENT

Khách hàng số 2

Tên khách hàng : Huỳnh Văn Tuấn Tài khoản số : 2219

Cân nợ trước : 135.00

Chi trả hiện hành : 135.00

Ngày trả (dd mm yyyy) : 10 8 1993

Cân nợ mới : 0.00

Tình trạng tài khoản : CURRENT

Khách hàng số 3

Tên khách hàng : Lê Tấn Sĩ Tài khoản số : 8452

Cân nợ trước : 387.42

Chi trả hiện hành : 35.00

Ngày trả (dd mm yyyy) : 4 7 1993

Cân nợ mới : 352.42

Tình trạng tài khoản : OVERDUE

Khách hàng số 4

Tên khách hàng : Tạ Quang Trinh Tài khoản số : 711

Cân nợ trước : 260.00

Chi trả hiện hành : 0

Cân nợ mới : 260.00

Tình trạng tài khoản : DELINQUENT

Sau khi chương trình chạy xong, tập tin old.dat sẽ bị xoá hoặc lưu trữ và tập tin new.dat sẽ được đổi tên thành old.dat. Việc này nhằm để tạo ra một tập tin mới sẽ được cập nhật trong tương lai. Trong ví dụ này chúng ta giả thiết rằng tập tin old.dat đã có sẵn, do vậy chúng ta không cần thảo luận thêm tập tin này phải tạo ra như thế nào lúc ban đầu. Sinh viên có thể xem đây là một bài tập.

3. Tập tin văn bản TOP

a. Khái niệm

Trong Pascal có một kiểu tập tin khác, đó là tập tin văn bản, có tên gọi là text file. Ðây là tập tin kiểu ký tự, tuy nhiên, tập tin text được cấu trúc thành các dòng, mỗi dòng được kết thúc bởi dấu hết dòng eoln (end-of-line).

Ðối với Turbo Pascal, dấu eoln được tạo bởi 2 ký tự điều khiển là CR (carriage return - về đầu dòng) và LF (line feed - nhảy xuống dòng tiếp). CR là chr(13) và LF là chr(10). Text file được kết thúc bởi dấu end-of-file, với Turbo Pascal dấu end-of-file là Ctrl-Z có mã ASCII là ký tự số 26.

Vì chiều dài của các dòng là khác nhau nên tập tin văn bản chỉ có thể truy xuất theo kiểu tuần tự. Ngoài ra, không thể tiến hành cả 2 hoạt động đọc và ghi cùng lúc trên text file. Ðể sử dụng text file, ta phải thực hiện các thủ tục ASSIGN, sau đó mở tập tin bằng các thủ tục RESET, REWRITE hoặc APPEND.

- REWRITE dùng để tạo ra một text file mới, sau đó ta chỉ có thể tiến hành ghi lên text file một cách tuần tự.

- RESET để mở một tập tin đã có và sau đó ta chỉ có thể đọc một cách tuần tự.

- Thủ tục APPEND(f) với f là biến text sẽ mở một tập tin đã có và sau đó ta chỉ có thể ghi thêm vào cuối tập tin.

- Cuối cùng đóng tập tin bằng thủ tục CLOSE, khi đó dấu kết thúc nội dung tập tin (Ctrl-Z ) sẽ được tự động đặt ở cuối tập tin.

Việc xuất nhập ký tự trên text file được thực hiện bằng các thủ tục READ và WRITE, việc xuất nhập các dòng được thực hiện bằng WRITELN (ghi theo dòng) và READLN (đọc theo dòng).

Mặc dù trong text file chứa các ký tự văn bản nhưng các thủ tục Read, Readln, Write, Writeln vẫn có khả năng đọc, ghi các số nguyên, số thực, giá trị logic hoặc chuỗi ký tự nhờ có sự chuyển đổi thích hợp các giá trị này với dòng ký tự.

b. Cách khai báo

Các biến tập tin văn bản có thể được khai báo một cách trực tiếp như sau:

c. Một số hàm và thủ tục trên text file

Các thủ tục và hàm đã trình bày trong phần trước (mục V.2.c) sau đều có thể sử dụng đối với tập tin văn bản:

• Thủ tục ASSIGN(Filevar, FileName)

• Thủ tục REWRITE(FileVar)

• Thủ tục RESET(FileVar)

• Thủ tục FLUSH(FileVar)

• Thủ tục CLOSE(FileVar)

• Thủ tục ERASE(FileVar)

• Thủ tục RENAME(FileVar, NewName)

• Hàm EOF(FileVar)

Ngoài ra, tập tin văn bản có thêm các hàm và thủ tục sau:

* Ghi dữ liệu vào tập tin văn bản

Có 3 mẫu để ghi dữ liệu vào text file là:

• WRITE(FileVar, Item1, Item2, ..., ItemN) ;

Ghi giá trị các phần tử Item1, Item2,..., ItemN lên tập tin được đại diện bởi FileVar.

• WRITELN(FileVar, Item1, Item2, ..., ItemN) ;

Giống như thủ tục WRITE, nhưng sau đó sẽ ghi thêm dấu kết thúc dòng (các ký tự điều khiển: CR và LF)

• WRITELN(FileVAR) ;

Chỉ làm một việc là ghi lên tập tin dấu kết thúc dòng.

FileVar là một biến kiểu text.

Item1, Item2, ..., ItemN là các mục ghi các hằng, biến, biểu thức của các kiểu vô hướng chuẩn, kiểu miền con và kiểu chuỗi.

Các thủ tục ghi vào text file không chấp nhận các kiểu ghi dữ liệu kiểu mảng (ARRAY), kiểu tập hợp (SET), kiểu bản ghi (RECORD) và kiểu tập tin (FILE).

Ví dụ 8.52

Program TepVanban;

Var f: TEXT;

Begin

Assign(f,'U:\Vanban.DAT');

Rewrite(f);

(1) Writeln(f,1,2,3);

(2) write(f,4,5);

(3) writeln(f);

(4) Close(f);

End.

Chương trình sẽ tạo ra tệp U:\Vanban.Dat có dạng như sau:

* Ðọc dữ liệu từ tập tin văn bản:

Việc đọc dữ liệu từ tệp văn bản được thực hiện hoàn toàn tương tự như việc đọc dữ liệu từ bàn phím, ta có 3 mẫu để đọc dữ liệu từ text file là:

- READ(FileVar, Variable1, Variable2, ..., VariableN) ;

- READLN(FileVar, Variable1, Variable2, ..., VariableN) ;

- READLN(FileVAR) ;

FileVar là một biến kiểu text.

Variable1, Variable2, ..., VariableN là các biến thuộc các kiểu vô hướng chuẩn, kiểu miền con và kiểu chuỗi.

Thủ tục Readln(FileVar, Variable1, Variable2, ..., VariableN) ; sẽ đưa con trỏ xuống hàng tiếp sau khi đọc hết các biến tương ứng. Thủ tục Readln(FileVar) ; xuống dòng tiếp theo mà không đọc gì hết.

Các thủ tục đọc dữ liệu từ text file không chấp nhận các dữ liệu kiểu mảng (ARRAY), kiểu tập hợp (SET), kiểu bản ghi (RECORD) và kiểu tập tin (FILE).

Ví dụ 8.53: Chương trình đếm số dòng của một tệp văn bản:

Program Dem_dong;

Var f:TEXT; sodong:integer;

Begin

sodong:=0;

{$I-}

Assign(f,'U:\VanBan.DAT');

Reset(f);

{$I+}

if IOResult<>0 then

begin Writeln(' Tệp này không tồn tại '); halt(1); end

esle

While Not EOF(f) Do

INC(sodong);

readln(f); {Xuống dòng kế tiếp}

end;

Close(f);

Writeln(' Số dòng đã đếm được là :' ,sodong);

Readln;

End.

* Hàm EOLN(FileVar)

Hàm EOLN(f) có giá trị là true nếu như con trỏ của text file f đang ở vào ký tự điều khiển CR của dấu end-of-line hoặc khi end-of-file có giá trị là true.

* Hàm SEEKEOLN(FileVar)

Hàm này tương tự như hàm EOLN nhưng trước khi kiểm tra EOLN nó nhảy qua các dấu cách Space và Tab.

* Hàm SEEKEOF(FileVar)

Hàm này tương tự như hàm EOF nhưng trước khi kiểm tra EOF nó nhảy qua các dấu cách Space và Tab và dấu cách dòng.

* Thủ tục APPEND(FileVar)

Thủ tục mở tập tin đã có và cho phép nối thêm vào cuối tập tin.

Ví dụ 8.54:

Chương trình Turbo Pascal chuyển các nội dung một tập tin văn bản thành một tập tin khác theo cơ sở từng dòng một (line-by-line)

PROGRAM Chuyen_file ;

VAR oldtext, newtext : text ;

x : char ; y : integer ; z : real ;

BEGIN

Assign (oldtext, 'text1.dat' ) ;

Assign (Newtext, 'Text2.dat') ;

Reset (oldtext) ;

Rewrite (Newtext) ;

WHILE NOT eof(oldtext) DO

BEGIN

WHILE NOT eoln(oldtext) DO

BEGIN {chuyển 1 dòng của văn bản}

Read (oldtext, x, y, z) ;

Write(' newtext, x, y, z ') ;

END ;

Readln (oldtext) ;

Writeln (' Newtext ') ;

END ;

Close (oldtext) ;

Close (newtext) ;

END.

Trong chương trình trên, chú ý là 2 tập tin băn bản có tên lần lượt là oldtext và newtext. Tên tập tin ngoại trú của chúng tương ứng là text1.dat và text2.dat. Trong oldtext, mỗi dòng chứa một hạng mục dữ liệu kiểu ký tự (char), một dữ liệu kiểu số nguyên (integer) và một dữ liệu kiểu số thực (real). Khi newtext được tạp ra thì nó cũng sẽ có cấu trúc bên trong giống như oldtext.

Vòng WHILE - DO bên trong thực hiện thao tác đọc một dòng văn bản từ oldtext và viết vào newtext. Vòng WHILE - DO bên ngoài thực hiện thao tác lặp lại trên cơ sở từng dòng cho đến khi nhận được chỉ thị end-of-file trong oldtext.

d. Các thiết bị ngoại vi coi như text file

Từ Turbo Pascal version 3.0 trở đi, chúng ta có thể sử dụng các câu lệnh gọi các thiết bị ngoại vi như các text file nghĩa là việc xuất nhập được thực hiện bằng các thao tác như với text file. Các file chuẩn (không cần khai báo lại) trong bảng sau có thể được dùng:

FileVar Thiết bị ngoại vi Công dụng

INPUT

OUTPUT

LPT1

LPT2

LPT3

LST

PRN

KBD

CON Bàn phím

Màn hình

Cổng máy in số 1

Cổng máy in số 2

Cổng máy in số 3

Máy in

Cổng máy in số 1

Bàn phím

Bàn phím, Máy in Nhập cơ bản

Xuất cơ bản

Chỉ có thể xuất

Chỉ có thể xuất

Chỉ có thể xuất

Chỉ có thể xuất

Chỉ có thể xuất

Chỉ có thể nhập

Có thể nhập, xuất

Các file chuẩn trên không cần dùng các thủ tục ASSIGN, REWRITE, RESET, CLOSE. Ðặc biệt đối với INPUT và OUTPUT không cần khai báo thủ tục trong READ/READLN và WRITE/WRITELN do vậy ta đọc từ bàn phím là đọc từ INPUT và ghi ra màn hình tức dùng OUTPUT.

Ví dụ 8.55: Chương trình in các số từ 1 đên 10 ra máy in.

Program mayIn;

Uses printer;

Const N=10;

Var f:text;

i:integer;

Begin

assign(f,'LPT1');

rewrite(f);

writeln( f,'Chuong trinh in ca so nguyen tu 1..10');

for i:=1 to N do

writeln(f,i:5);

close(f);

End.

e. Ví dụ ứng dụng

Ví dụ 8.56: Mã hoá và giải mã văn bản (Encoding and Decoding Text).

Chúng ta hãy xem xét một ứng dụng thú vị hơn liên quan đến việc sử dụng text file: lập một chương trình trên Turbo Pascal có khả năng mã hóa và giải mã một văn bản có nhiều dòng. Chương trình tạo ra một thực đơn (menu), người sử dụng có thể chọn các hạng mục sau:

• Nhập text từ bàn phím, mã hoá văn bản đó và trữ đoạn văn bản đã được mã hoá vào text file.

• Lấy ra đoạn văn bản đã mã hoá và thể hiện nó ra màn hình.

• Lấy ra đoạn văn bản đã mã hoá, giải mã nó và thể hiện đoạn văn đã giải mã ra màn hình.

• Chấm dứt chương trình.

Ðoạn văn bản nhập vào có thể có nhiều dòng. Những dòng này sẽ được duy trì như văn bản được mã hóa, lưu trữ, giải mã và thể hiện. Tuy nhiên, việc mã hoá và giải mã thực sự xảy ra trên cơ sở từng ký tự (character-by-character basis).

Ðể mã hoá và giải mã đoạn văn, người sử dụng phải nhập vào một khoá ký tự đơn (Chương trình sẽ nhắc việc này nếu mục 1 và 3 trong menu được chọn). Mỗi ký tự sẽ được mã hóa bằng cách thêm vào mã số (numerial code) của nó thành mã số của khoá, sau đó xác định ký tự đại diện bởi thuật toán tổng như sau:

z := chr(ord(x) + ord(key))

trong đó z là ký tự đã mã hoá tương đương với ký tự gốc x.

Khi cộng hai mã số với nhau, chú ý rằng tổng số không được vượt qua 127, đây là số lớn nhất mã ký tự của ASCII chuẩn. Do đó, nếu tổng z vượt quá 127 thì giá trị của z phải được điều chỉnh bằng cách trừ đi 127 từ giá trị tổng. Như vậy, ta có thể viết:

y := ord(x) + ord(key) ;

IF y > 127 then y := y - 127 ;

z := chr(y) ;

Với cách này, chương trình có thể vận hành suông sẻ và dễ thực hiện. Tuy nhiên cũng cần hiểu rằng, giá trị nào đó của z có thể cũng tạo ra các ký tự không in ra được (như là line feed - nhảy dòng tiếp, carriage returns - về đầu dòng, beep - tiếng bíp, v.v...) dù là các ký tự này sẽ được đặt ở dạng mã hóa. Như thế, đoạn văn mã hóa có thể xuất hiện rất thất thường khi thể hiện trên màn hình.

Ðoạn văn mã hóa có thể được giải mã trở lại bằng cách làm ngược tiến trình trên, đó là:

y := ord(z) - ord(key) ;

IF y < 0 THEN y := y + 127 ;

x := chr(y) ;

Khi giải mã đoạn văn, một cảm nhận là chúng ta sử dụng cùng một giá trị từ của khoá như khi chúng ta đã dùng khi mã hoá đoạn văn gốc. Nếu không, đoạn văn giải mã sẽ bị bóp méo.

Chương trình Turbo Pascal toàn bộ được trình bày ở dưới. Trong chương trình này, thủ tục menu sẽ cho các câu nhập/xuất cần thiết để tạo ra menu, nếu người sử dụng chọn 1 hoặc 3, thì có dấu nhắc cho một từ khóa đơn (a single-character key). Chú ý rằng thủ tục này sẽ dùng 2 biến số: choice, theo kiểu đặc trưng (features type), và key, theo kiểu ký tự (char type).

Khi chạy chương trình, dữ liệu nhập sẽ liên tục cho đến khi một dấu chấm dứt end-of-file được nhập vào từ bàn phím. Cách tạo dấu chấm dứt này phải khác với các dấu của hệ điều hành. Ở đây, chúng ta dùng Ctrl-Z cho mục đích này. Chương trình nguyên văn của Byron S. Gottfried:

PROGRAM encode;

(* ENTER TEXT FROM AN INPUT DEVICE, ENCODE IT, STORE THE ENCODED TEXT IN A TEXT FILE AND DISPLAY THE TEXT, EITHER ENCODED OR DECODED *)

TYPE features = 1 .. 4;

VAR code : text;

x, z, key : char;

y : - 127 .. 254;

choice : features;

PROCEDURE menu (VAR choice : features; VAR key : char);

(* Generate a menu and return the user's choice *)

BEGIN

writeln;

writeln(' E N C O D I N G / D E C O D I N G T E X T ');

writeln;

writeln(' Program Features: ');

writeln;

writeln(' 1 - Enter text, encode and store ');

writeln;

writeln(' 2 - Retrieve encoded text and display ');

writeln;

writeln(' 3 - Retrieve encoded text, decode and display ');

writeln;

writeln(' 4 - End computation ');

writeln;

write(' Please enter your selection (1, 2, 3 or 4) -> ');

readln(choice);

writeln;

IF (choice = 1) OR (choice = 3) THEN

BEGIN

write('Please enter the key (one character) -> ');

readln(key);

writeln;

END;

END; (* menu *)

BEGIN (* Main action statements *)

Assign(code, 'code.dat');

choice := 1;

WHILE choice <> 4 DO

BEGIN

menu(choice, key);

CASE choice OF

1 : BEGIN (* enter text, encode and store *)

rewrite(code);

writeln(' Enter text ');

writeln;

WHILE NOT eof(code) DO

BEGIN

WHILE NOT eoln(code) DO

BEGIN

read(x);

y := ord(x) + ord(key);

IF y > 127 THEN y := y - 127;

z := chr(y);

write(code, z);

END;

Readln;

Writeln(Code);

END; (* NOT eof *)

Reset(input); (* eof = false *)

END;

2,3 : BEGIN (* retrieve encoded text, decode and display *)

reset(code);

WHILE NOT eof(code) DO

BEGIN

WHILE NOT eoln(code) DO

BEGIN

Read(code, z);

IF choice = 2 THEN x := z

ELSE

BEGIN

y := ord(z) - ord(key);

IF y<0 THEN y:=y +127;

x := chr(y);

END;

write(x);

END; (* NOT eoln *)

Readln(code);

writeln;

END (* NOT eof *)

END;

4 : ;

END; (* CASE *)

END; (* WHILE choice <> 4 *)

Writeln;

Writeln(' Goodbye, Have A Nice Day ! ');

Close(Code);

END.

Khối chương trình chính là cấu trúc dễ nhận thấy là CASE với 4 lựa chọn khác nhau. Lựa chọn thứ nhất, tương ứng với choice = 1, cung cấp sự hướng dẫn nhập các dòng dữ liệu, sau đó mã hóa và lưu trữ mỗi dòng trên cơ sở từng ký tự một. Chú ý rằng các ký được mã hóa được viết thành text file có tên là code (tập tin ngoại trú là code.dat). Sau khi việc nhập dữ liệu hoàn tất, tập tin văn bản input được reset theo đó eof đặt ở điều kiện false lần nữa.

Nếu choice có giá trị là 2 hoặc 3, thì tập tin văn bản code được reset và đoạn văn đã mã hóa được đọc trên cơ sở từng ký tự một. Nếu choice = 2, tập tin văn bản đã mã hóa được đọc trực tiếp đến thiết bị xuất. Nếu choice = 3, các ký tự sẽ được giải mã thành đoạn văn giống như chúng được đọc và sau đó đoạn văn được ghi ra thiết bị xuất.

Khi chạy chương trình, thực đơn sau xuất hiện:

E N C O D I N G / D E C O D I N G T E X T

Program Features:

1 - Enter text, encode and store

2 - Retrieve encoded text and display

3 - Retrieve encoded text, decode and display

4 - End computation

Please enter your selection (1, 2, 3 or 4) ->

Nếu giá trị 1 hoặc 3 được chọn nhập vào, câu nhắc sau sẽ hiện để nhận từ khóa:

Please enter the key (one character) ->

Bây giờ, giả sử người sử dụng nhập vào các dòng text. Người sử dụng trước tiên phải chọn số 1. Giả sữ người sử dụng nhập ký tự C để làm khóa, theo sau là text:

All digital computers, regardless of their size, are

basically electronic devives that can transmit, store and

manipulate information (i.e., data)

<Ctrl-Z>

(Kýtự Ctrl-Z ở cuối văn bản đại diện cho dấu chấm dứt end-of-file). Thông tin này sẽ được mã hóa, mỗi ký tự một lần, sau đó được trữ trong tập tin văn bản text. Sau tiến trình này, thực đơn chính lại xuất hiện.

Nếu người sử dụng bây giờ chọn số 2, dữ liệu đã mã hóa sau sẽ được đọc từ tập tin văn bản code và thể hiện ra màn hình.

*00c(-+-8%0c'31498)67oc6)+%6(0)77c3*c8,)-6c7->)oc%6)

&%7-'%00=c)0)'8632-'c( ):-')7c8,%8c'%2c86%271-8oc7836)c%2(

1%2-490%8)c-2*361%8-32ck-q)qoc(%8%1q

Dĩ nhiên, đoạn mã này được mã hóa từ đoạn văn bản đã nhập ở trên.

Bây giờ giả sử người sử dụng chọn số 3. Dấu nhắc cho từ khóa sẽ xuất hiện lần nữa. Người sử dụng cần phải gõ vào chữ C, giống như từ khóa đã dùng để mã hóa ở trên. Khi từ khóa đã nhập đúng, dữ liệu đã mã hóa một lần nữa sẽ đọc lại tập tin văn bản code, giải mã trên cơ sở từng ký tự một, sau cùng sẽ thể hiện trên màn hình như sau:

All digital computers, regardless of their size, are

basically electronic devives that can transmit, store and

manipulate information (i.e., data)

Như thế, đoạn văn gốc được tái cấu trúc lại.

Nếu nhập từ khóa sai tại chỗ dấu nhắc, đoạn văn sẽ giải mã thành đoạn văn sai và méo mó. Giả sử, người sử dụng gõ chữ B thay vì chữ C. Kết quả là đoạn văn lộn xộn, méo mó sau sẽ xuất hiện thay thế cho đoạn văn đúng của nó:

Bmm!ejhjubm!dpnqvufst-!sfhbsemftt!pg!uifjs!tj{f-!bsf

cbtjdbmmz!fmfduspojd!efwjdft!uibu!dbo!usbotnju-!tupsf!boe

nbojqvmbuf!jogpsnbujpo!)j/f/-!ebub*/

Cuối cùng, nếu ngưới sử dụng chọn số 4 để ngưng làm việc với chương trình, câu chào sau sẽ xuất hiện:

Goodbye, Have a Nice Day !

_______________________________________________________________________________________________

PHỤ CHƯƠNG

ÐỒ HỌA TRONG TURBO PASCAL

VII. KHÁI NIỆM VỀ ĐỒ HỌA.

VIII. KHỞI ĐỘNG CHẾ ĐỘ ĐỒ HỌA.

IX. LỔI ĐỒ HỌA.

X. MỘT SỐ HÀM VÀ THỦ TỤC CHUẨN TRONG UNIT GRAPH.

1. MÀU VÀ MẩU(KIểU).

2. ĐIểM, KIểU ĐƯờNG THẳNG, ĐƯờNG THẳNG.

3. CÁC HÌNH KHÔNG TÔ.

4. CÁC HÌNH CÓ TÔ.

5. Xử LÝ CHUỗI KÝ Tự TRÊN MÀN HÌNH Đồ HọA.

6. CửA Sổ TRONG CHế Độ Đồ HọA.

7. ĐÓNG CHế Độ Đồ HọA.

XI. MỘT VÀI VÍ DỤ MINH HỌA.

PHầN NÀY CHỉ NHằM GIớI THIệU MộT Số CÁC KHÁI NIệM CƠ BảN Về CHế Độ Đồ HọA, CÁC THủ TụC VÀ HÀM Để: KHởI ĐộNG CHế Độ Đồ HọA, Vẽ CÁC HÌNH CƠ BảN NHƯ ĐƯờNG THẳNG, ĐƯờNG TRÒN, CUNG ELIP, HÌNH QUạT, ĐA GIÁC, CHUỗI KÝ Tự, CửA Sổ VIEWPORT,... ĐÃ ĐƯợC KHAI BÁO SẵN TRONG UNIT GRAPH CủA TURBO PASCAL.

I. KHÁI NIỆM VỀ ÐỒ HỌA TOP

TRONG TURBO PASCAL CÓ HAI CHế Độ THƯờNG ĐƯợC Sử DụNG ĐÓ LÀ: CHế Độ VĂN BảN (TEXT MODE) VÀ CHế Độ Đồ HọA (GRAPHICS MODE). CHế Độ VĂN BảN THÌ MÀN HÌNH ĐƯợC THIếT LậP HIểN THị 25 DÒNG VÀ 80 CộT NHƯ ĐÃ GIớI THIệU ở PHầN TRƯớC. CÒN ở CHế Độ Đồ HọA, THÌ MÀN HÌNH LạI ĐƯợC THIếT LậP DựA TRÊN TừNG ĐIểM ảNH (PIXEL), MỗI MÀN HÌNH GồM NHIềU ĐIểM ảNH ĐƯợC SắP XếP TRÊN CÁC ĐƯờNG THẳNG NằM NGANG VÀ ĐƯờNG THẳNG ĐứNG, Sự BÀI TRÍ Số PIXEL TRÊN MỗI LOạI MÀN HÌNH ĐƯợC GọI LÀ Độ PHÂN GIảI (RESOLUTION), Độ PHÂN GIảI CÀNG CAO THÌ Số PIXEL CÀNG NHIềU VÀ HÌNH ảNH CÀNG MịN.

Hệ TọA Độ CHO MỗI LOạI MÀN HÌNH CÓ KHÁC NHAU (XEM BảNG 1), CHẳNG HạN ở LOạI MÀN HÌNH VGA LÀ 640X480 NHƯ HÌNH SAU:

MộT CHƯƠNG TRÌNH Đồ HọA THƯờNG GồM CÁC PHầN SAU:

• KHởI TạO CHế Độ Đồ HọA.

• XÁC ĐịNH MÀU NềN, MÀU VÀ KIểU CHữ, MÀU ĐƯờNG Vẽ, MÀU TÔ VÀ KIểU TÔ.

• Vẽ VÀ TÔ CÁC HÌNH TA CầN THựC HIệN.

• CÁC THAO TÁC KHÁC NHƯ CHO HIệN DÒNG CHữ, CHÚ THÍCH.

• ÐÓNG CHế Độ Đồ HọA Để TRở Về CHế Độ VĂN BảN.

II. KHỞI ÐỘNG CHẾ ÐỘ ÐỒ HỌA TOP

Ðể CÓ THể THựC HIệN CHƯƠNG TRÌNH TRÊN CHế Độ Đồ HọA, TRƯớC TIÊN TA PHảI KHởI ĐộNG CHế Độ Đồ HọA. VIệC NÀY ĐƯợC THÔNG QUA THủ TụC SAU:

PROCEDURE INITGRAPH(VAR GRAPHDRIVER:INTEGER; VAR GRAPHMODE: INTEGER; PATHTODRIVER: STRING);

VớI: GRAPHDRIVER, GRAPHMODE LÀ LOạI MÀN HÌNH VÀ MốT MÀN HÌNH (XEM BảNG 1).

PATHTODRIVER LÀ ĐƯờNG DẫN CủA THƯ MụC CHứA CÁC TậP TIN ĐIềU KHIểN Đồ HọA.

VÍ Dụ: GIả Sử TA CÓ LOạI MÀN HÌNH VGA, MốT LÀ VGAHI, CÁC TậP TIN ĐIềU KHIểN Đồ HọA ở TRONG THƯ MụC F:\WINAPPS\BP\BGI, TA CÓ THể VIếT PHầN CHƯƠNG TRÌNH KHởI ĐộNG Đồ HọA NHƯ SAU:

USES GRAPH;

VAR MH,MODE: INTEGER; {MH: LOạI MÀN HÌNH}

BEGIN

MH:=VGA; MODE:=VGAHI; {CÓ THể DÙNG HằNG DETECT CHO MH } (1)

INITGRAPH(MH,MODE,' F:\WINAPPS\BP\BGI');

...........

END.

VIệC NHớ CÁC LOạI MÀN HÌNH VÀ MốT MÀN HÌNH LÀ ĐIềU GÂY Dễ NHầM LẫN, DO VậY TA CÓ THể Để CHO MÁY Tự ĐộNG DÒ TÌM LOạI VÀ MốT MÀN HÌNH. NHƯ VậY ở CHƯƠNG TRÌNH TRÊN TA Bỏ DÒNG (1) THÌ KHI THựC HIệN MÁY Sẽ Tự ĐộNG DÒ TÌM (DECTECT), ĐÂY LÀ ĐIểM RấT HAY VÌ NÓ Sẽ CHO KHởI ĐộNG LOạI MÀN HÌNH ĐANG Sử DụNG VÀ MốT Đồ HọA CÓ Độ PHÂN GIảI CAO NHấT.

BảNG 1: CÁC GIÁ TRị CủA GRAPDRIVER, GRAPHMODE VÀ RESOLUTION CủA MộT Số LOạI MÀN HÌNH THÔNG DụNG.

GRAPHDRIVER GRAPHMODE RESOLUTION

DETECT (0) Ðộ PHÂN GIảI CAO NHấT VÀ LOạI MÀN HÌNH Sử DụNG

CGA (1) CGAC0 (0)

CGAC1 (1)

CGAC2 (2)

CGAC3 (3)

CGACHI (4) 320 X 200

320 X 200

320 X 200

320 X 200

640 X 200

EGA (3) EGALO (0)

EGAHI (1) 640 X 200

640 X3 50

VGA (9) VGALO (0)

VGAMED (1)

VGAHI (2) 640 X 200

640 X 350

640 X 480

CHÚ Ý: Ở BảNG NÀY CÁC HằNG DETECT CÓ GIÁ TRị 0, HằNG VGA CÓ GIÁ TRị 9, HằNG VGAHI

CÓ GIÁ TRị 2, ...

TA CÓ THể XÁC ĐịNH GRAPDRIVER, GRAPHMODE VÀ RESOLUTION TRÊN MộT MÁY ĐANG HOạT ĐộNG BằNG CHƯƠNG TRÌNH SAU:

USES GRAPH;

VAR MH,MODE:INTEGER;

BEGIN

MH:=DETECT; {CÓ THể Bỏ DÒNG NÀY}

INITGRAPH(MH,MODE,'F:\WINAPPS\BP\BGI');

WRITELN('GRAPHDRIVER = ',MH,' GRAPHMODE = ',MODE,' RESOLUTION =',GETMAXX,

'X',GETMAXY);

READLN;

CLOSEGRAPH;

END.

VớI: GETMAXX, VÀ GETMAXY LÀ CÁC HÀM TRả Về GIÁ TRị LớN NHấT TƯƠNG ứNG CHO HÀNG, CộT CủA MÀN HÌNH VÀ MốT HIệN HÀNH.

III. LỖI ÐỒ HỌA TOP

KHI KHởI ĐộNG Đồ HọA, NếU MÁY KHÔNG TÌM THấY CÁC CHƯƠNG TRÌNH ĐIềU KHIểN Đồ HọA THÌ Sẽ PHÁT SINH RA LỗI Đồ HọA VÀ NHƯ VậY CHƯƠNG TRÌNH KHÔNG THể THựC HIệN ĐƯợC HOặC CÓ THể TREO MÁY.

TRONG Cả 2 TRƯờNG HợP CÓ HOặC KHÔNG CÓ LỗI, TA NÊN Sử DụNG HÀM GRAPHRESULT Để BIếT CÓ LỗI HAY KHÔNG? CÓ THể KếT HợP VớI HÀM GRAPHERRORMSG Để NHậN ĐƯợC THÔNG BÁO ĐÚNG NHấT. BảNG 2 LIệT KÊ MộT Số THÔNG BÁO LỗI THƯờNG GặP:

BảNG 2: CÁC LỗI Đồ HọA

HằNG TRị MÃ LỗI THÔNG TIN LỗI PHÁT HIệN

GROK

GRNOINITGRAPH

GRNOTDETECTED

GRFILENOTFOUND

GRINVALIDDRIVER

GRNOLOADMEM

GRNOSCANMEM

GRNOFLOODMEM

GRFONTNOFOUND

GRNOFONTMEM

GRINVALIDMODE

GRERROR

GRIOERROR

GRINVALIDFONT

GRINVALIDFONTNUM

0

-1

-2

-3

-4

-5

-6

-7

-8

-9

-10

-11

-12

-13

-14 KHÔNG CÓ LỗI KHởI ĐộNG Đồ HọA.

CHƯA KHởI ĐộNG Dồ HọA.

KHÔNG CÓ PHầN CứNG Đồ HọA.

KHÔNG TÌM THấY TRÌNH ĐIềU KHIểN Đồ HọA.

TRÌNH ĐIềU KHIểN KHÔNG HợP Lệ.

KHÔNG Đủ Bộ NHớ RAM CHO Đồ HọA.

TRÀN VÙNG NHớ TRONG SCAN FILL.

TRÀN VÙNG NHớ TRONG FLOOD FILL.

KHÔNG TÌM THấY TậP TIN FONT.

KHÔNG Đủ Bộ NHớ RAM Để NạP FONT.

KIểU Đồ HọA KHÔNG HợP Lệ CHO TRÌNH ĐIềU KHIểN.

LỗI Đồ HọA TổNG QUÁT.

LỗI Đồ HọA VÀO RA.

TậP TIN FONT KHÔNG HợP Lệ.

Số HIệU ĐạI DIệN CHO FONT KHÔNG HợP Lệ.

VÍ Dụ: CHƯƠNG TRÌNH KIểM TRA QUÁ TRÌNH KHởI ĐộNG Đồ HọA VÀ NếU CÓ LỗI Sẽ THÔNG BÁO LỗI RA MÀN HÌNH:

USES GRAPH;

VAR

MALOI: INTEGER;

GRDRIVER, GRMODE: INTEGER;

BEGIN

GRDRIVER := DETECT;

INITGRAPH(GRDRIVER, GRMODE, 'F:\WINAPPS\BP\BGI');

MALOI := GRAPHRESULT; { CHECK FOR ERRORS }

IF MALOI <> GROK THEN

BEGIN

WRITELN('LỗI Đồ HọA LÀ : ', GRAPHERRORMSG(MALOI));

WRITELN('LỗI!, THOÁT KHỏI CHƯƠNG TRÌNH ...');

HALT(1); {LệNH NGắT NGANG VÀ KếT THÚC CHƯƠNG TRÌNH }

END

ELSE { PHầN CHƯƠNG TRÌNH CầN THựC HIệN KHI KHÔNG CÓ LỗI Đồ HọA }

END.

IV. MỘT SỐ HÀM VÀ THỦ TỤC CHUẨN TRONG UNIT GRAPH

1. MầU VÀ MẫU (KIểU) TOP

ÐốI VớI MÀN HÌNH TRắNG ĐEN (HERCULES MONOCHROME) TA CÓ 2 GIÁ TRị MÀU 0 VÀ 1, CÒN ĐốI VớI CÁC MÀN HÌNH MÀU (VGA, EGA,...) THÌ CÓ 16 GIÁ TRị MÀU Từ 0..15 ĐƯợC LIệT KÊ TRONG BảNG 3, VÀ KIểU TÔ MÀU THÌ CÓ 11 KIểU ĐÃ ĐƯợC ĐịNH SẵN LÀ Từ 0..11 VÀ MộT KIểU DO NGƯờI LậP TRÌNH ĐịNH NGHĨA (USER - DEFINED FILL) VÀ CÁC KIểU TÔ ĐƯợC LIệT KÊ TRONG BảNG 4.

A. THủ TụC CHọN MÀU ĐƯờNG Vẽ

SETCOLOR(COLORNUM:WORD);

B. THủ TụC ĐặT MÀU NềN CủA MÀN HÌNH

SETBKCOLOR(COLORNUM :WORD);

C. THủ TụC CHọN KIểU TÔ VÀ MÀU TÔ

SETFILLSTYLE(PATTERN:WORD; COLORNUM:WORD);

D. HÀM NHậN MÀU TRả Về DO THủ TụC SETCOLOR ĐÃ ĐặT MÀU TRƯớC ĐÓ

GETCOLOR: WORD;

E. HÀM NHậN MÀU NềN TRả Về DO THủ TụC SETBKCOLOR ĐÃ ĐặT TRƯớC ĐÓ

GETBKCOLOR: WORD;

F. HÀM TRả Về GIÁ TRị MÀU LớN NHấT

GETMAXCOLOR: WORD;

BảNG 3: CÁC GIÁ TRị CÓ THể NHậN CủA BIếN MÀU COLORNUM

TÊN HằNG GIÁ TRị MÀU MÀU HIểN THị

BLACK

BLUE

GREEN

CYAN

RED

MAGENTA

BROWN

LIGHTGRAY

DARKGRAY

LIGHTBLUE

LIGHTGREEN

LIGHTCYAN

LIGHTRED

LIGHTMAGENTA

YELLOW

WHITE 0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

ÐEN

XANH DA TRờI

XANH LÁ CÂY

XANH LƠ

Ðỏ

TÍM

NÂU

XÁM NHạT

XÁM SẫM

XANH DA TRờI NHạT

XANH LÁ CÂY NHạT

XANH LƠ NHạT

Ðỏ NHạT

TÍM NHạT

VÀNG

TRắNG

BảNG 4: CÁC GIÁ TRị CÓ THể NHậN CủA BIếN KIểU TÔ PATTERN

TÊN HằNG GIÁ TRị KIểU TÔ DIễN GIảI KIểU TÔ

EMPTYFILL

SOLIDFILL

LINEFILL

LTSLASHFILL

SLASHFILL

BKSLASHFILL

LTBKSLASHFILL

HATCHFILL

XHATCHFILL

INTERLEAVEFILL

WIDEDOTFILL

CLOSEDOTFILL

USERFILL 0

1

2

3

4

5

6

7

8

9

10

11

12

TÔ BằNG MÀU NềN

TÔ ĐặC

TÔ BằNG GạCH NGANG

TÔ BằNG ///

TÔ BằNG /// IN ĐậM

TÔ BằNG \\\ IN ĐậM

TÔ BằNG \\\

TÔ BằNG ĐƯờNG GạCH BÓNG NHạT

TÔ BằNG ĐƯờNG GạCH BÓNG CHữ THậP

TÔ BằNG ĐƯờNG ĐứT QUÃNG

TÔ BằNG NHữNG DấU CHấM THƯA

TÔ BằNG DấU CHấM DÀY

MẫU TÔ Tự TạO

2. ÐIểM, KIểU ĐƯờNG THẳNG, ĐƯờNG THẳNG TOP

A. THủ TụC Vẽ MộT ĐIểM TạI MộT TọA Độ (X,Y)

PUTPIXEL(X,Y:INTEGER;COLORNUM:WORD);

B. THủ TụC CHọN KIểU ĐƯờNG THẳNG

SETLINESTYPE(LINESTYLE:WORD;PATTERN:WORD;THICKNESS:WORD);

VớI THAM Số LINESTYLE CÓ CÁC GIÁ TRị NHƯ BảNG 5 SAU:

BảNG 5: THAM Số LINESTYLE

HằNG GIÁ TRị DIễN GIảI

SOLIDLN

DOTTEDLN

CENTERLN

DASHEDLN

USERBITLN

0

1

2

3

4 NÉT ĐậM

NÉT CHấM

NÉT CHấM GạCH

NÉT GạCH

MẫU Tự TạO

VớI THAM Số THICKNESS CÓ CÁC GIÁ TRị NHƯ BảNG 6 SAU:

BảNG 6: THAM Số THICKNESS

HằNG GIÁ TRị DIễN GIảI

NORMWIDTH

THICKWIDTH 1

3

Bề DÀY BÌNH THƯờNG

Bề DÀY ĐậM

C. THủ TụC Vẽ ĐƯờNG THẳNG Từ TọA Độ (X1 ,Y1) ĐếN TọA Độ (X2 ,Y2)

LINE(X1 ,Y1 ,X2 ,Y2: INTEGER);

3. CÁC HÌNH KHÔNG TÔ TOP

A. THủ TụC Vẽ HÌNH CHữ NHậT

RECTANGLE(X1 ,Y1 ,X2 ,Y2:INTEGER);

B. THủ TụC Vẽ HÌNH TRÒN

CIRCLE(X,Y:INTEGER;R:WORD);

VớI X,Y LÀ TọA Độ TÂM, R LÀ BÁN KÍNH.

C. THủ TụC Vẽ MộT CUNG TRÒN

ARC(X,Y:INTEGER; STANGLE,ENDANGLE:WORD; R :WORD);

VớI STANGLE LÀ GÓC BắT ĐầU, ENDANGLE LÀ GÓC KếT THÚC.

D. THủ TụC Vẽ CUNG ELLIPSE HOặC MộT ELLIPSE

ELLIPSE(X,Y:INTEGER;STANGLE,ENDANGLE:WORD;XRADIUS,YRADIUS:WORD);

NếU STANGLE = 0 VÀ ENDANGLE =360 THÌ LÀ MộT HÌNH ELLIPSE, NếU

ENDANGLE < 360 THÌ LÀ MộT CUNG ELLIPSE.

4. CÁC HÌNH CÓ TÔ TOP

A. ÐƯờNG GấP KHÚC (ĐA GIÁC)

MUốN Vẽ MộT ĐƯờNG GấP KHÚC ĐI QUA N ĐIểM TọA Độ : (X1 ,Y1), (X2 ,Y2),..., (XN ,YN ) THÌ TA PHảI ĐƯA TạO Độ N ĐIểM NÀY VÀO MộT MảNG POLY NÀO ĐÓ MÀ MỗI PHầN Tử CủA MảNG CÓ KIểU POINTTYPE ĐÃ ĐƯợC ĐịNH NGHĨA SẵN NHƯ SAU:

TYPE

POINTTYPE = RECORD

X,Y : INTEGER;

END;

KHI ĐIểM CUốI (XN ,YN ) CÓ TọA Độ TRÙNG VớI ĐIểM ĐầU (X1 ,Y1 ) THÌ N ĐIểM NÀY Sẽ TạO THÀNH MộT ĐƯờNG GấP KHÚC KHÉP KÍN.

DÙNG THủ TụC DRAWPOLY(N, POLY); Để Vẽ ĐƯờNG GấP KHÚC ĐI QUA N TọA Độ ĐÃ ĐịNH SẵN TRONG MảNG POLY.

DÙNG THủ TụC FILLPOLY(N,POLY); Để Vẽ VÀ TÔ ĐƯờNG GấP KHÚC ĐI QUA N TọA Độ ĐÃ ĐịNH SẵN TRONG MảNG POLY.

VÍ Dụ:

USES GRAPH;

CONST

GK: ARRAY[1..3] OF POINTTYPE=((X:5;Y:200),(X:190;Y:5),(X:100;Y:300)); {GấP KHÚC}

GKKK: ARRAY[1..4] OF POINTTYPE = ((X:405;Y:200),(X:590;Y:5),(X:500;Y:300),(X:405;Y:200));

VAR GD, GM: INTEGER;

BEGIN

GD := DETECT;

INITGRAPH(GD, GM, 'F:\WINAPPS\BP\BGI');

IF GRAPHRESULT <> GROK THEN HALT(1);

SETBKCOLOR(CYAN);

SETCOLOR(YELLOW);

SETFILLSTYLE(SOLIDFILL,MAGENTA);

DRAWPOLY(3,GK);

FILLPOLY(3,GK); {ĐƯờNG GấP KHÚC KHÔNG KHÉP KÍN}

FILLPOLY(4,GKKK); {ĐƯờNG GấP KHÚC KHÉP KÍN}

READLN;

CLOSEGRAPH;

END.

B. THủ TụC Vẽ HÌNH CHữ NHậT

BAR(X1 ,Y1 ,X2 ,Y2: INTEGER);

C. THủ TụC Vẽ HÌNH HộP CHữ NHậT

BAR3D(X1 ,Y1 ,X2 ,Y2:INTEGER; DEPTH:WORD;TOP:BOOLEAN);

THAM Số DEPTH: LÀ Số ĐIểM TRÊN Bề SÂU CủA KHốI 3 CHIềU.

THAM Số TOP CÓ 2 GIÁ TRị ĐƯợC ĐịNH NGHÃ SẵN LÀ: TOPON (TRUE) TƯƠNG ứNG KHốI 3 CHIềU Sẽ CÓ NắP VÀ TOPOFF (FALSE) Sẽ ứNG VớI KHốI 3 CHIềU KHÔNG CÓ NắP (XEM HÌNH Vẽ).

D. THủ TụC Vẽ HÌNH ELLIPSE

FILLELLIPSE(X,Y:INTEGER;XRADIUS,YRADIUS:WORD);

E. THủ TụC Vẽ HÌNH QUạT

PIESLICE(X,Y:INTEGER; STANGLE,ENDANGLE,RADIUS:WORD);

5. Xử LÝ CHUỗI KÝ Tự TRÊN MÀN HÌNH Đồ HọA TOP

A. THủ TụC NạP FONT CHữ

CÁC FONT CHữ NằM TRONG CÁC TậP TIN CÓ PHầN Mở RộNG LÀ .CHR . Ðể NạP CÁC FONT CHữ NÀY TA DÙNG THủ TụC:

SETTEXTSTYLE(FONT,DIRECTION,CHARSIZE:WORD);

VớI: THAM Số FONT CÓ THể NHậN MộT TRONG CÁC GIÁ TRị SAU:

HằNG DEFAUFONT HAY GIÁ TRị 0

HằNG TRIPLEXFONT HAY GIÁ TRị 1

HằNG SMALLFONT HAY GIÁ TRị 2

HằNG SANSSERIFFONT HAY GIÁ TRị 3

HằNG GOTHICFONT HAY GIÁ TRị 4

THAM Số DIRECTION CÓ THể NHậN MộT TRONG CÁC GIÁ TRị SAU:

HằNG HORIZDIR HAY GIÁ TRị 0

HằNG VERTDIR HAY GIÁ TRị 1

THAM Số CHARSIZE LÀ Cỡ KÝ Tự VÀ NÓ CÓ THể NHậN MộT TRONG CÁC GIÁ TRị Từ 1 ĐếN 10.

B. THủ TụC ĐặT CHế Độ CĂN CHỉNH CHUỗI VĂN BảN

SETTEXTJUSTIFY(HORIZ, VERT :WORD);

TRONG ĐÓ:

THAM Số HORIZ CÓ THể LÀ MộT TRONG CÁC HằNG: LEFTTEXT, CENTERTEXT, RIGHTTEXT.

THAM Số VERT CÓ THể LÀ MộT TRONG CÁC HằNG: BOTTOMTEXT, CENTERTEXT, TOPTEXT.

C. THủ TụC HIểN THị CHUỗI VĂN BảN TạI Vị TRÍ CON NHÁY

OUTTEXT (TEXT:STRING);

D. THủ TụC HIểN THị CHUỗI VĂN BảN TạI TọA Độ (X,Y)

OUTTEXTXY (X,Y:INTEGER;TEXT:STRING);

6. CửA Sổ TRONG CHế Độ Đồ HọA (VIEWPORT) TOP

Ðể THIếT LậP MộT CửA Sổ TRÊN MÀN HÌNH Đồ HọA, TA Sử DụNG ĐếN CHứC NĂNG CủA VIEWPORT. CửA Sổ VIEWPORT ĐƯợC XEM NHƯ MộT VÙNG CHữ NHậT TRÊN MÀN HÌNH Độ HọA, NÓ GIốNG NHƯ THủ TụC WINDOW TRONG CHế Độ VĂN BảN (TEXT MODE), NGHĨA LÀ TA CÓ THể HIệN MộT DÒNG VĂN BảN, Vẽ HÌNH HOặC XÓA CHỉ NằM GọN TRONG VIEWPORT ĐÃ ĐịNH, TA CÓ THể MINH HọA MộT CửA Sổ VIEWPORT NHƯ HÌNH SAU:

Ðể HIểU RÕ CÁCH THIếT LậP MộT VIEWPORT, TA HÃY XÉT ĐếN CÁCH KHAI BÁO KIểU CủA VIEWPORT NHƯ SAU:

VIEWPORTTYPE = RECORD

X1, Y1, X2, Y2 : INTEGER;

CLIP : BOOLEAN;

END;

TRONG ĐÓ: (X1, Y1) , (X2, Y2) LầN LƯợT LÀ GÓC TọA Độ TRÊN BÊN TRÁI VÀ TọA Độ GÓC DƯớI BÊN PHảI, MÀ CHÚNG PHảI THỏA TÍNH CHấT SAU:

CLIP LÀ MộT BIếN TRƯờNG CÓ KIểU BOOLEAN MÀ NÓ CÓ Ý NGHĨA NHƯ SAU:

• NếU CÓ GIÁ TRị BằNG TRUE (HAY BằNG HằNG CLIPON) THÌ KHÔNG CHO PHÉP Vẽ BÊN NGOÀI VIEWPORT.

• NếU CÓ GIÁ TRị BằNG FALSE (HAY BằNG HằNG CLIPOFF) THÌ CHO PHÉP Vẽ BÊN NGOÀI VIEWPORT.

A. THủ TụC THIếT LậP MộT VIEWPORT

SETVIEWPORT(X1,Y1,X2,Y2:INTEGER; CLIP:BOOLEAN);

SAU KHI THIếT LậP VIEWPORT TA Sẽ CÓ MộT Hệ TọA Độ MớI MÀ GÓC TRÊN BÊN TRÁI CủA VIEWPORT Sẽ CÓ TọA Độ (0,0).

VÍ Dụ: NHƯ HÌNH Vẽ ở TRÊN (GIả Sử TA CHọN CLIP BằNG HằNG CLIPON) THÌ TA PHảI THIếT LậP VIEWPORT NHƯ SAU: SETVIEWPORT(300,250,500,350,CLIPON);

* TọA Độ ÂM DƯƠNG

VớI MộT Số Đồ THị CủA TOÁN HọC PHảI CÓ TọA Độ ÂM DƯƠNG, Để Vẽ NÓ TA PHảI CHIA MÀN HÌNH RA LÀM 4 PHầN TƯƠNG ứNG VớI 4 VÙNG (I, II, III, IV) ÂM DƯƠNG CủA MộT Hệ TRụC TọA Độ XY. Ðể THựC HIệN VIệC NÀY, TA PHảI DÙNG ĐếN CửA Sổ VIEWPORT, VớI CÁCH THIếT LậP SAO CHO TọA Độ (0,0) CủA TRụC TọA Độ XY LÀ TÂM TUYệT ĐốI CủA MÀN HÌNH GÓC TRÊN BÊN TRÁI CủA VIEWPORT NHƯ SAU:

- ÐặT: X1= GETMAXX; Y1= GETMAXX;

X2= GETMAXX; Y2= GETMAXX;

- DÙNG THủ TụC SETVIEWPORT(X1,Y1,X2,Y2,CLIPOFF) , VớI CLIP = CLIPOFF Để CÓ THể Vẽ RA NGOÀI GIớI HạN CủA VIEWPORT.

VÍ Dụ:

Vẽ Đồ THị HÀM SIN(X) TRONG Hệ TRụC TọA Độ ÂM DƯƠNG, VớI HOÀNH Độ

PROGRAM DOTHI;

USES CRT,GRAPH;

CONST

SCALEX=20;

SCALEY=80;

VAR MH,MODE,X,Y,I:INTEGER;

BEGIN

INITGRAPH(MH,MODE,'F:\WINAPPS\BP\BGI');

SETVIEWPORT(GETMAXX DIV 2,GETMAXY DIV 2,GETMAXX,GETMAXY,CLIPOFF);

SETCOLOR(BLUE);

LINE(-(GETMAXX DIV 2),0,GETMAXX DIV 2,0);

LINE(0,-(GETMAXY DIV 2),0,GETMAXY DIV 2);

SETTEXTJUSTIFY(CENTERTEXT,CENTERTEXT);

SETCOLOR(WHITE);

OUTTEXTXY(-GETMAXX DIV 4,-GETMAXX DIV 4,'DO THI HINH SIN ');

SETCOLOR(RED);

OUTTEXTXY(GETMAXX DIV 2- 32,2,'TRUC X >');

OUTTEXTXY(27,-(GETMAXY DIV 2-5),'^ TRUC Y');

OUTTEXTXY(0,0,'0,0');

FOR I:= -400 TO 400 DO

BEGIN

X:=ROUND(2*PI*I* SCALEX /200);

Y:=ROUND(SIN(2*PI*I/200)* SCALEY);

PUTPIXEL(X,Y,YELLOW);

END;

REPEAT UNTIL KEYPRESSED;

CLOSEGRAPH;

END.

B. THủ TụC NHậN VIEWPORT HIệN HÀNH

GETVIEWSETTINGS(VAR VIEWPORT: VIEWPORTTYPE);

C. THủ TụC XÓA BÊN TRONG MÀN HÌNH VIEWPORT HIệN HÀNH

CLEARVIEWPORT;

THủ TụC XÓA SạCH TấT Cả CÁC PHầN (HÌNH Vẽ, CHUỗI KÝ Tự, ...) BÊN TRONG VIEWPORT VÀ ĐƯA CON TRỏ Về TọA Độ (0,0) CủA CửA Sổ VIEWPORT HIệN HÀNH.

D. THủ TụC XÓA SạCH MÀN HÌNH Đồ HọA

CLEARDEVICE;

7. ÐÓNG CHế Độ Đồ HọA TOP

Ðể TRở Về CHế Độ VĂN BảN, TA DÙNG THủ TụC:

CLOSEGRAPH;

V. MỘT VÀI VÍ DỤ MINH HỌA TOP

VÍ Dụ 1: Vẽ BầU TRờI ĐầY SAO

PROGRAM VEBAUTROI;

USES GRAPH,CRT;

VAR GD,GM,X,Y:INTEGER; MAXCOLOR:WORD;

BEGIN

GD:=DETECT;

INITGRAPH(GD,GM,'C:\TP70\BGI');

IF GRAPHRESULT<> GROK THEN HALT(1);

X:=GETMAXX; Y:=GETMAXY;

MAXCOLOR:=GETMAXCOLOR;

RANDOMIZE;

WHILE (NOT KEYPRESSED) DO

BEGIN DELAY(100);

PUTPIXEL(RANDOM(X),RANDOM(Y),

RANDOM (MAXCOLOR-1)+1);

END;

CLOSEGRAPH;

END.

VÍ Dụ 2: Vẽ Đồ THị CÁC HÀM Số: SIN(X),COS(X) VÀ ARCTAN(X)

PROGRAM VEDOTHI;

USES

CRT,GRAPH;

VAR

MH,MODE:INTEGER;CHON:CHAR;CHUGIAI:STRING;

PROCEDURE CHONHAM;

BEGIN

WRITELN('CÁC Đồ THị CÓ THể:');

WRITELN('1---->Ðồ THị HÌNH SIN(X)');

WRITELN('2---->Ðồ THị HÌNH COS(X)');

WRITELN('3---->Ðồ THị HÌNH ARCTAN(X)');

WRITE('CHọN Đồ THị NÀO ?'); READLN(CHON);

CASE CHON OF

'1': CHUGIAI:=ÐỒ THỊ HÀM SIN;

'2': CHUGIAI:=ÐỒ THỊ HÀM COS;

'3': CHUGIAI:=ÐỒ THỊ HÀM ARCTAN;

END;

END;

FUNCTION F(CHON:CHAR;X:REAL):REAL;

BEGIN

CASE CHON OF

'1': F:=SIN(X);

'2': F:=COS(X);

'3': F:=ARCTAN(X);

END;

END;

PROCEDURE DOTHI(A,B:REAL;X1,Y1,X2,Y2:INTEGER;MN,MD:INTEGER);

VAR FX,K,H,R,C,D:REAL;

X,Y,N,M,I:INTEGER;

BEGIN

C:=F(CHON,A); D:=F(CHON,A);

R:=A; H:=(B-A)/1000;

WHILE R <= B DO

BEGIN

FX:=F(CHON,R);

IF C>FX THEN C:=FX;

IF D<FX THEN D:=FX;

R:=R+H;

END;

SETCOLOR(MD);SETBKCOLOR(MN);

N:=X2-X1;

H:=(B-A)/N;

M:=Y2-Y1;

K:=(D-C)/M;

FOR I:=0 TO N DO

BEGIN

X:=X1+I;

FX:=F(CHON,A+I*H);

Y:=ROUND((FX-C)/K)+Y1;

Y:=Y2-Y+Y1;

IF I=0 THEN MOVETO(X,Y)

ELSE LINETO(X,Y);

END;

END;

BEGIN (* CHƯƠNG TRÌNH CHÍNH *)

CLRSCR;

CHONHAM;

MH:=DETECT;

INITGRAPH(MH,MODE,'U:\BGI');

SETVIEWPORT(GETMAXX DIV 2,GETMAXY DIV 2,GETMAXX,GETMAXY,CLIPOFF);

LINE(-(GETMAXX DIV 2),0,GETMAXX DIV 2,0);

LINE(0,-(GETMAXY DIV 2),0,GETMAXY DIV 2);

SETTEXTJUSTIFY(CENTERTEXT,CENTERTEXT);

OUTTEXTXY(-GETMAXX DIV 4,-GETMAXX DIV 4,CHUGIAI);

SETCOLOR(RED);

OUTTEXTXY(GETMAXX DIV 2- 32,2,'TRUC X >');

OUTTEXTXY(27,-(GETMAXY DIV 2-5),'^ TRUC Y');

OUTTEXTXY(0,0,'0,0');

DOTHI(-4*PI,4*PI,-(GETMAXX DIV 2)+100,-(GETMAXY DIV 2)+100,GETMAXX DIV 2 -100,

GETMAXY DIV 2 - 100,MAGENTA,YELLOW);

READLN;

CLOSEGRAPH;

END.

TÓM TẮT

CÁC CÚ PHÁP TRONG NGÔN NGỮ PASCAL

Niklaus Writh, cha đẻ của ngôn ngữ Pascal, đã đề xuất ra việc xây dựng các sơ đồ mô tả tóm tắt các cú pháp của ngôn ngữ lập trình Pascal. Sơ đồ cú pháp (syntax diagram) sẽ giúp ta nắm được các dạng thức của những yếu tố cấu trúc chương trình. Một số các phần tử trong sơ đồ có ý nghĩa sau:

Ở sơ đồ tên (danh hiệu) phải bắt đầu bằng một chữ cái, sau đó có thể có nhiều chữ, nhiều số và gạch dưới.

Ở sơ đồ biến, một biến bất kỳ có thể là biến có chỉ số hay không có chỉ số. Mọi biến đều có thể nhận diện qua một tên được đặt theo qui tắc đặt tên, nếu có chỉ số thì mỗi chỉ số có dạng một biểu thức, cách nhau bằng dấu phẩy ',' và tất cả chỉ số được đặt trong dấu ngoặc vuông '[' và ']'

I. SƠ ÐỒ CẤU TRÚC CĂN BẢN

TẠM KẾT

Ðến đây, các bạn sinh viên đã đi qua một chặng đầu cơ bản nhưng rất quan trọng trong việc trang bị cho mình một kiến thức cần thiết trong khoa học máy tính. Bài giảng này, dĩ nhiên do giới hạn về thời lượng học tập, không thể trình bày hết các chi tiết của từng phần và cần phải tiếp tục bổ sung nhất là phần thực hành. Nếu các bạn nắm vững các điều đã học thì các bạn có nhiều cơ may tự học hỏi, tìm tòi và khám phá nhiều điều thú vị khác.

Hiện nay, trên thị trường sách và thư viện ở Việt nam, các bạn có thể kiếm nhiều tài liệu bổ ích viết bằng tiếng Việt hoặc tiếng nước ngoài để bổ xung cho kiến thức của các bạn. Lãnh vực tin học tuy mới mẻ ở Việt nam nhưng rất có nhiều hứa hẹn phát triển trong tương lai.

Chúc các bạn nhiều sức khoẻ và thành công.

TÁC GIẢ

Lê Anh Tuấn

Thạc sĩ Kỹ thuật

Continue Reading

You'll Also Like

864K 35.4K 109
Tác giả: Ngã Ái Cật Băng Bổng Tình trạng tác phẩm gốc: Hoàn thành (99 chương + 11 ngoại truyện) Tình trạng edit: Hoàn thành (28/04/2024) Thể loại: Na...
427K 22K 104
Tên gốc: 欲言难止 Tác giả: Mạch Hương Kê Ni Nguyên tác: Trường Bội Edit: Cấp Ngã Giang Sơn (Gin) Thể loại: ABO, gương vỡ lại lành, yêu thầm thành thật, H...
206K 11.3K 40
Huấn văn, BL, 1x1, Hiện đại, Niên thượng, Gương vỡ lại lành, HE Đã hoàn thành phần Truyện Chính (Phần 1). Phần 2 sẽ được viết lẻ tẻ không liên kết qu...
60.4K 4.5K 87
Chỉ là mấy suy nghĩ của mình về các nhân vật và tình tiết trong series này thôi:D Cũng để kỷ niệm sự trở lại của Bungou Stray Dogs với season 4 *Upda...