Tải bản đầy đủ (.doc) (43 trang)

Chuyển biểu thức dạng trung tố ra dạng hậu tố tương ứng

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (283.98 KB, 43 trang )

Mục lục
Mục lục.............................................................................................................................................1
I. Giới thiệu về đề tài:.......................................................................................................................2
1.2. ”KÝ PHÁP NGHỊCH ĐẢO BA LAN PHƯƠNG PHÁP TÍNH GIÁ TRỊ BIỂU THỨC
TOÁN HỌC”................................................................................................................................2
1.2.1. Các phương pháp biểu diễn phép toán hai ngôi:................................................................2
1.2.2. Cây biểu diễn biểu thức......................................................................................................3
1.2.3. Thế nào là ký pháp nghịch đảo Ba Lan?...........................................................................4
II. Cấu trúc dữ liệu:...........................................................................................................................5
III. Thuật toán cơ bản:......................................................................................................................5
IV. Mở rộng đề tài:...........................................................................................................................8
VI. Kết quả chương trình:...............................................................................................................40
VII. Kết luận:..................................................................................................................................41
VIII. Nhận xét của giáo viên hướng dẫn:......................................................................................42


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

I. Giới thiệu về đề tài:
1.1. Tên đề tài: Chuyển biểu thức dạng trung tố ra dạng hậu tố tương
ứng
1.2. ”KÝ PHÁP NGHỊCH ĐẢO BA LAN PHƯƠNG PHÁP TÍNH GIÁ TRỊ
BIỂU THỨC TOÁN HỌC”
Khi lập trình, tính giá trị một biểu thức toán học là điều bình thường. Tuy nhiên,
trong nhiều ứng dụng (như chương trình vẽ đồ thị hàm số chẳng hạn, trong đó chương
trình cho phép người dùng nhập vào hàm số), ta cần phải tính giá trị của một biểu thức


được nhập vào từ bàn phím dưới dạng một chuỗi. Với các biểu thức toán học đơn giản
(như a+b) thì bạn có thể tự làm bằng các phương pháp tách chuỗi “thủ công”. Nhưng để
“giải quyết” các biểu thức có dấu ngoặc, ví dụ như (a+b)*c + (d+e)*f , thì các phương
pháp tách chuỗi đơn giản đều không khả thi. Trong tình huống này, ta phải dùng đến Ký
Pháp Nghịch Đảo Ba Lan (Reserve Polish Notation – RPN), một thuật toán “kinh điển”
trong lĩnh vực trình biên dịch.
Để đơn giản cho việc minh họa, ta giả định rằng chuỗi biểu thức mà ta nhận được
từ bàn phím chỉ bao gồm: các dấu mở ngoặc/đóng ngoặc; 4 toán tử cộng, trừ, nhân và chia
(+, -, *, /); các toán hạng đều chỉ là các con số nguyên từ 0 đến 9; không có bất kỳ khoảng
trắng nào giữa các ký tự.

1.2.1. Các phương pháp biểu diễn phép toán hai ngôi:
Một phép toán hai ngôi trên tập hợp X là một ánh xạ f: X×X →X cho (a,b) f(a,b)
A. Ánh xạ f khi đó thường được ký hiệu bởi *, được gọi là toán tử, các phần tử a, b
được gọi là các hạng tử (còn gọi là toán hạng).
Khi viết biểu thức biểu diễn phép toán đó ta có thể đặt ký hiệu toán tử ở trước, sau
hoặc giữa các toán hạng (là biến hoặc hằng). Thông thường trong các biểu thức đại và số
học, ta viết kí hiệu phép toán giữa hai hạng tử( trung tố). Ví dụ : a +b, a * b, ... Khi một
biểu thức có nhiều phép toán, ta dùng các cặp dấu ngoặc "(", ")" và thứ tự ưu tiên các
phép toán để chỉ rõ thứ tự thực hiện các phép toán. (Các phép toán đều quy về phép toán 2
ngôi.)
Ta cũng có thể viết hai hạng tử trước và kí hiệu toán tử sau (hậu tố). Chẳng hạn:
a + b viết là a b +, a*b viết là a b *
Cũng có thể viết toán tử trước, hai toán hạng sau( tiền tố). Chẳng hạn:

SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06




Trang: 2




ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao

GVHD: Phan Thanh

a + b viết là + a b, a * b viết là * a b

1.2.2. Cây biểu diễn biểu thức
Dùng cây biểu diễn biểu thức có thể thấy rõ trình tự tính toán biểu thức.
Ví dụ: a*(b+c)-d^5
Được thực hiện theo sơ đồ biểu diễn bởi cây nhị phân sau:

/ \
* ^
/ \ /\
a + d 5
/ \
b c
Ta mô tả quá trình đọc, ghi nhận giá trị và thực hiện phép tính, giống như quá trình duyệt
cây biểu thức theo thứ tự giữa như sau:
1.
2.
3.
4.
5.

6.
7.
8.
9.

Đọc và ghi nhận giá trị biến a(con trái)
Đọc và ghi nhận loại phép toán * (con trái)
Đọc giá trị biến b (con trái)
Đọc và ghi nhận loại phép toán + (con phải)
Đọc giá trị biến c (con phải) thực hiện phép cộng b+c=x và ghi nhận kết quả x,thực
hiện phép nhân a*x = y và ghi nhận kết quả y
Đọc và ghi nhận phép toán Đọc giá trị biến d
Đọc và ghi nhận phép toán ^
Đọc giá trị 5 và thực hiện phép lũy thừa z=d^5, thực hiện phép trừ k=y-z

Để đơn giản ta giả sử các phép toán đều là hai ngôi. Khi đó cây biểu thức là cây nhị phân
đầy đủ. Quy tắc thực hiện phép toán trên cây như sau:


Hoặc duyệt cây theo thứ tự giữa mỗi lần duyệt con phải nếu đã tính được giá trị tại
con đó thì thực hiện phép tính quy định bởi toán tử ghi tại đỉnh cha.
Viết tuần tự các đỉnh duyệt theo thứ tự giữa thì ta có dãy

/

\

*
^
/ \ /\

a +d 5
/ \
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06

/ \
+
^
/\
/\
* c d 5
/ \


Trang: 3




ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao

b

c

a

GVHD: Phan Thanh


b

a * b + c - d ^ 5 (1)
Theo cách này, mỗi khi duyệt một đỉnh
1. Nếu là lá bên trái (biến hoặc hằng) thì ghi nhận giá trị của biến
2. Nếu là toán tử thì lưu dạng của toán tử
3. Nếu là con phải và lá thực hiện phép tính theo toán tử đã lưu ở đỉnh cha giữa các
giá trị đã lưu tại con phải và giá trị mới đọc tại con trái, ghi kết quả vào đỉnh cha.


Hoặc duyệt cây biểu thức trên theo thứ tự sau: Mỗi đỉnh được duyệt sau khi cả hai
đỉnh con đã được duyệt. Khi đó mỗi khi duyệt một đỉnh trong ta thực hiện toán tử
ghi tại đỉnh này với giá trị của hai con của đỉnh ấy.
Thứ tự duyệt các đỉnh khi đó sẽ là:
a b c + * d 5 ^ - (2)

Khi duyệt theo thứ tự sau, việc thực hiện phép tính tiến hành theo quy tắc:
Mỗi khi thăm một đỉnh thì:
1. Nếu là lá thì ghi nhận giá trị của biến
2. Nếu là đỉnh trong thì thực hiện toán tử ghi ở đỉnh này vào các giá trị ghi ở hai đỉnh
con, ghi kết quả vào chính đỉnh này.
Với dãy (1) nếu không dùng đến dấu ngoặc, có thể có hai cây biểu thức khác nhau cho
cùng một dãy đỉnh khi duyệt thứ tự giữa. Còn với dãy (2) cùng với lưu ý rằng các biến và
hằng luôn được biểu diễn bằng các lá, các toán tử luôn biểu diễn bởi các nút trong, từ mỗi
dãy dạng (2) ta luôn dựng lại được cây biểu thức duy nhất theo giải thuật sau: Khi độ dài
dãy lớn hơn 1, duyệt từ bên trái sang, nếu gặp một phần tử là toán tử, thì lấy hai phần tử
đứng trước nó ra khỏi dãy chuyển thành hai con của phần tử ấy (theo đúng thứ tự). Vì thế
biểu thức viết bởi dãy (2) là hoàn toàn xác định.

1.2.3. Thế nào là ký pháp nghịch đảo Ba Lan?

Cách trình bày biểu thức theo cách thông thường tuy tự nhiên với con người
nhưng lại khá “khó chịu” đối với máy tính vì nó không thể hiện một cách tường minh quá
trình tính toán để đưa ra giá trị của biểu thức. Để đơn giản hóa quá trình tính toán này, ta
phải biến đổi lại biểu thức thông thường về dạng hậu tố - postfix (cách gọi ngắn của thuật
ngữ ký pháp nghịch đảo Ba Lan). Để phân biệt hai dạng biểu diễn biểu thức, ta gọi cách
biểu diễn biểu thức theo cách thông thường là trung tố - infix (vì toán tử nằm ở giữa hai
toán hạng).
Ký pháp nghịch đảo Ba Lan được phát minh vào khoảng giữa thập kỷ 1950 bởi
Charles Hamblin - một triết học gia và khoa học gia máy tính người Úc - dựa theo công
trình về ký pháp Ba Lan của nhà Toán học người Ba Lan Jan Łukasiewicz. Hamblin trình
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 4




ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao

GVHD: Phan Thanh

bày nghiên cứu của mình tại một hội nghị khoa học vào tháng 6 năm 1957 và chính thức
công bố vào năm 1962.
Từ cái tên hậu tố các bạn cũng đoán ra phần nào là theo cách biểu diễn này, các
toán tử sẽ được đặt sau các toán hạng. Cụ thể là biểu thức trung tố: 4+5 sẽ được biểu diễn
lại thành 4 5 +.


II. Cấu trúc dữ liệu:
Ta xây dựng kiểu cấu trúc toantu theo kiểu stack (ngăn xếp) gồm các trường:
+
Trường tt kiểu char để chứa các toán tử và hàm.
+
Trường next kiểu con trỏ cấu trúc toantu chỉ đến phần tử kế tiếp.
struct toantu
{
char tt;
toantu *next;
};

Kiểu cấu trúc thứ hai là hauto theo kiểu danh sách liên kết đơn gồm:
+
Trường letter kiểu mảng kí tự để chứa các kí hiệu của toán hạng, toán tử ,
hàm.
+
Trường value kiểu double để chứa giá trị thực của các toán hạng.
+
Trường next kiểu con trỏ cấu trúc hauto để chỉ đến phần tử kế tiếp.
struct hauto
{
char letter[10];
double value;
hauto *next;
};
typedef toantu *stack;
typedef hauto *list;
stack S;

list P;

III. Thuật toán cơ bản:
Chuyển đổi từ trung tố sang hậu tố
Thuật toán chuyển đổi này được phát minh bởi vị giáo sư người Đức nổi tiếng
Edsger Dijkstra (cũng là tác giả của thuật toán tìm đường đi ngắn nhất được đặt theo tên
ông và semaphore, một kỹ thuật để đồng bộ các tiến trình trong lập trình đa nhiệm). Thuật
toán này cũng dựa theo cơ chế ngăn xếp. Ý tưởng chung của thuật toán cũng là duyệt biểu
thức từ trái sang phải:
Nếu gặp một toán hạng (con số ) thì ghi nó vào chuỗi kết quả (chuỗi kết quả
là biểu thức trung tố).
Nếu gặp dấu mở ngoặc, đưa nó vào stack.
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 5




ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao

GVHD: Phan Thanh

-

Nếu gặp một toán tử (gọi là o1 ), thực hiện hai bước sau:

*
Chừng nào còn có một toán tử o2 ở đỉnh ngăn xếp. Và độ ưu tiên của
o1 nhỏ hơn hay bằng độ ưu tiên của o2 thì lấy o2 ra khỏi ngăn xếp và
ghi vào kết quả.
*
Push o1 vào ngăn xếp
Nếu gặp dấu đóng ngoặc thì cứ lấy các toán tử trong ngăn xếp ra và ghi vào
kết quả cho đến khi lấy được dấu mở ngoặc ra khỏi ngăn xếp.
Khi đã duyệt hết biểu thức trung tố, lần lượt lấy tất cả toán tử (nếu có) từ
ngăn xếp ra và ghi vào chuỗi kết quả.
Để dễ hiểu, bạn hãy quan sát quá trình thực thi của thuật toán qua một ví dụ cụ thể sau:
Biểu thức cần chuyển đổi: 3+4*2/(1-5)
Kí tự
3
+
4
*
2
/
(
1
5
)

*

Thao tác
Ghi 3 vào hậu tố
Push + vào stack
Ghi 4 vào hậu tố

Push * vào stack
Ghi 2 vào hậu tố
Lấy * ra khỏi stack, ghi vào
kết quả, và push / vào stack
Push ( vào stack
Ghi 1 vào hậu tố
Push – vào stack
Ghi 5 vào hậu tố
Pop cho đến khi lấy được ( ,
ghi các toán tử pop được ra
hậu tố
Pop tất cá các toán tử ra
khỏi ngăn xếp và ghi vào
hậu tố.

Stack
+
+
+*
+*
+/

Chuỗi hậu tố
3
3
34
34
342
342*


+/(
+/(
+/(+/(+/

342*
342*1
342*1
342*15
342*15342*16-/+

Hàm khởi tạo cho stack S và list P:
void create();

*

Hàm kiểm tra stack rỗng:

*

Hàm đẩy các toán tử vào stack S:

*

Hàm lấy ra một phần tử trên đỉnh stack đồng thời xoá phần tử đó ra
khỏi stack:

*

Hàm xem phần tử trên đỉnh stack nhưng không xoá phần tử đó:


int empty();
void push(char x)

char pop();
char top();

*

Hàm chèn một phần tử vào cuối danh sách list P:
void insert_last(char *x,double val);

*

Hàm kiểm tra độ ưu tiên của các toán tử:
int ref(char x)

SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 6


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh


{
if ( ( x == '+')|| ( x =='-')) return 1;
else if ( ( x == '*')|| ( x== '/')) return 2;
else if (x=='(' || x==')' ) return 0
else return -1;
}

*

Hàm kiểm soát các kí tự nhập vào
int test(char x)
{
if ((x>='0')&&(x<='9')) return 1;
else if(x== '+' ||x=='-'||x=='*'||x=='/')return 2;
else if ( x=='(' || x==')'|| x==' ' )return 4;
else return 0;
}

*

Hàm chuyển biểu thức trung tố sang hậu tố:
void convert(char *expr)
{
char temp[10];
int n,l,i;
create();
n=strlen(expr);
i=0;
while(i

{
while(expr[i]==' '&&iif(expr[i]=='(') push(expr[i]);
else if(expr[i]==')')
{
while(top()!='(')
{
temp[0]=pop();
temp[1]=’\0’;
insert_last(temp,1);
}
pop();
}
else if(test(expr[i])==1)
{
l=0;
temp[l]=expr[i];
l++;
temp[l]='\0';
insert_last(temp,expr[i]);
}
else if(test(expr[i])==2)
{
if(empty())
push(expr[i]);

SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06




Trang: 7




ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao

GVHD: Phan Thanh

else
{
while(ref(top())>=ref(expr[i]))
{
temp[0]=pop();
temp[1]=’\0’;
insert_last(temp,1);
}
push(expr[i]);
}
}
i++;
}
if(!empty())
{
while(S!=NULL)
{
temp[0]=pop();
temp[1]=’\0’;

insert_last(temp,1);
};
}
return;
}

Dĩ nhiên là thuật toán được trình bày trên đây là khá đơn giản và chưa ứng dụng
được trong trường hợp biểu thức có các hàm như sin, cos,… Tuy nhiên, việc mở rộng
thuật toán bao gồm số thực và các hàm sin,cos, .... xin trình bày trong phần sau.

IV. Mở rộng đề tài:
Như vậy phần thuật toán cơ bản đã nêu lên nguyên lý chung trong việc giải quyết
chuyển một biểu thức từ dạng trung tố sang hậu tố đối với các toán tử +,- ,*, / số có một
chữ số. Trong phần mở rộng này sẽ nói mở rộng hơn bao gồm cả số thực ( số có nhiều
chứ sô), các hàm mũ, sin, cos, tg, ln..., việc kiểm tra một biểu thức có đúng không, và tính
toán một biểu thức nếu biểu thức đúng.
*
Khi ta xét biểu thức có liên qua tới hàm tức là sẽ phải nhập các kí tự
nên ta sẽ thêm vào trong hàm kiểm tra các kí tự nhập ở đầu vào như sau:
int test(char x)
{
if (((x>='0')&&(x<='9'))||(x== '.')) return 1;
else if (x== '+' ||x=='-'||x=='*'||x=='/'||x=='^'||x=='%'||x=='!') return 2;
else if (((x>=65)&&(x<=90)) || ((x>=97)&&(x<=122)) ) return 3;
else if ( x=='(' || x==')'|| x==' ' )return 4;
else return 0;
}
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06




Trang: 8


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

*
Theo các quy ước toán học, ta có được thứ tự ưu tiên của các phép toán
và các hàm. Giai thừa (!) có độ ư tiên lớn nhất đến mũ (^) có độ ưu tiên kém hơn
nhưng nó lại ưu tiên hơn so với hàm, kế tiếp sau hàm là các dạng toán tử một ngôi
mà ở đây ta dùng(#) để đánh dấu và các phép toán nhân (*) , chia (/) có độ ưu tiên
hơn các phép toán (+) , trừ (-).
int ref(char x)
{
if (( x == '+')||(x =='-')) return 1;
else if ( ( x=='*')||(x== '/')||( x=='%')) return 2;
else if( x=='#') return 3;
else if((x>='a')&&(x<='k')) return 4;
else if( x=='^') return 5;
else if( x=='!') return 6;
else if(x=='('||x==')') return 0;
else return -1;
}


*
Trong hàm convert (expr) này sẽ bổ sung việc xử lý số thực, các hàm
(sin, cos, ln, ^ ...), các toán hạng một ngôi ( a-+b, a* +b...).
+ Hàm menu_list( x) giúp ta rút ngắn bớt chương trình trong việc nhập chèn
các hàm hoặc các toán tử vào biểu thức hậu tố.
void menu_list(char x)
{
char temp[10];
switch(x)
{
case 'a': insert_last("sin",0); break;
case 'b': insert_last("cos",0); break;
case 'c': insert_last("tg",0); break;
case 'd': insert_last("cotg",0);break;
case 'e': insert_last("ln",0); break;
case 'f': insert_last("lg",0); break;
case 'g': insert_last("sqrt",0); break;
case 'h': insert_last("exp",0); break;
case 'k': insert_last("asin",0); break;
case 'l': insert_last("acos",0); break;
case 'm': insert_last("atan",0);break;
default :
temp[0]=x;
temp[1]='\0';
insert_last(temp,1);
}
return;
}

+

Hàm test_am (expr, i ) sẽ thực hiện nhiệm vụ kiểm tra một toán hạng có
phải là toán hạng một ngôi hay không. Hàm sẽ trả về 1 tức là toán hạng một ngôi và
0 không phải là toán hạng một ngôi.
int test_am ( char *expr,int i)
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 9


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

{
int j,ok=0,k,l,m;
char *temp,*temp1;
temp= new (char);
temp1=new( char);
if ( expr[i]=='+'||expr[i]=='-')
{
if ( i==0) j=i;
if ( i>0 ) j=i-1;
while ( expr[j]==' ') --j;
if ((( test (expr[j])==2)&&(( expr[j]=='%')||( expr[j]!='!')))||( expr[j]=='('))

ok=1;
if ( test (expr[j])==3)
{
k=0;
while ( test(expr[j])==3)
{
temp[k]=expr[j];
j--;
k++;
}
k--;
m=0;
for ( l=k; l >=0; l--)
{
temp1[m]=temp[l];
m++;
}
temp1[m]='\0';
if ( strcmpi(temp1,"pi") ==0 ) ok=0;
else if ( strcmpi(temp1,"sin") ==0 ) ok=1;
else if ( strcmpi(temp1,"cos") ==0 ) ok=1;
else if ( strcmpi(temp1,"tg") ==0 ) ok=1;
else if ( strcmpi(temp1,"cotg")==0 ) ok=1;
else if ( strcmpi(temp1,"ln") ==0 ) ok=1;
else if ( strcmpi(temp1,"lg") ==0 ) ok=1;
else if ( strcmpi(temp1,"sqrt")==0 ) ok=1;
else if ( strcmpi(temp1,"exp") ==0 ) ok=1;
else if ( strcmpi(temp1,"asin")==0 ) ok=1;
else if ( strcmpi(temp1,"acos")==0 ) ok=1;
else if ( strcmpi(temp1,"atan")==0 ) ok=1;

}
}
return ok;
}

* Hàm chuyển biểu thức trung tố sang hậu tố có mở rộng.
void convert(char *expr)
{
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 10


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

char temp[10],temp1[10];
int n,l,i,a,kt;
char x;
double val;
create();
n=strlen(expr);
i=0;

while(i{
while(expr[i]==' '&&iif(expr[i]=='(') push(expr[i]);
else if(expr[i]==')')
{
while(top()!='(')
{
temp[0]=pop();
menu_list(temp[0]);
}
pop();
}
else if(test(expr[i])==1)
{
l=0;
while(test(expr[i])==1)
{
temp[l]=expr[i];
i++; l++;
}
i--;
temp[l]='\0';
val=atof(temp);
insert_last(temp,val);
}
else if(test(expr[i])==2)
{
kt=test_am(expr,i);
if ( kt==1)

{
push(expr[i]);
push('#');
}
else
{
if(empty())
push(expr[i]);
else
{
while(ref(top())>=ref(expr[i]))
{
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 11


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

temp[0]=pop();
menu_list(temp[0]);
}

push(expr[i]);
}
}
}
else if(test(expr[i])==3)
{
l=0;
while(test(expr[i])==3)
{
temp[l]=expr[i];
l++; i++;
}
i--;
temp[l]='\0';
if(strcmpi(temp,"pi")==0 ) insert_last("PI", 3.14);
else
{
if(strcmpi(temp,"sin")==0) x='a';
else if(strcmpi(temp,"cos")==0) x='b';
else if(strcmpi(temp,"tg")==0) x='c';
else if(strcmpi(temp,"cotg")==0)x='d';
else if(strcmpi(temp,"ln")==0) x='e';
else if(strcmpi(temp,"lg")==0) x='f';
else if(strcmpi(temp,"sqrt")==0)x='g';
else if(strcmpi(temp,"exp")==0) x='h';
else if(strcmpi(temp,"asin")==0)x='k';
else if(strcmpi(temp,"acos")==0)x='l';
else if(strcmpi(temp,"atan")==0)x='m';
if(empty()) push(x);
else

{
if ( test(top())==3)push(x);
else
{
while(ref(top())>=ref(x))
{
temp[0]=pop();
menu_list(temp[0]);
}
push(x);
}
}
}
}
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 12


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

i++;

}
if(!empty())
{
while(S!=NULL)
{
temp[0]=pop();
menu_list(temp[0]);
};
}
return;
}

Phần tiếp theo sau đây, chúng tôi muốn mở rộng ra việc kiểm tra một biểu
thức có đúng chưa, và nếu đã đúng rồi thì hãy tính biểu thức đó.
+
Hàm kiểm tra test_bt( exp) thực hiện kiểm tra biểu thức vào có đúng
không, một số trường hợp biểu thức sai trong quá trình tính toán làm cho
điều kiện của một biểu thức bị vi phạm ( 5 / (2-2) làm cho mẫu số của biểu
thức bằng 0=> biểu thức sai, và điều này sẽ được kiểm tra trong quá trình
tính toán ở hàm calculate( ) ở phần sau)và hàm sẽ trả về giá trị 1 nếu biểu
thức đúng và trả về 0 nếu biểu thức sai.
Ví dụ:
2++ , ( ), 2..322 , 2.3! , sin 5+/ 1, (2+5)/2+(2 , 2^*3 ,...........
int test_bt( char *exp)
{
int i, j, m,k,l, ok=1,n=0, z=0;
char *temp,temp1[10];
float val, val1;
temp=new(char);
m=strlen(exp);

i=0;
while ( i{
if ( test(exp[i])==0)return 0;
while ( exp[i]==' ' && iif ( i==m) return 0;
if ( test(exp[i])==1)
{
k=0;
while( test(exp[i])==1)
{
temp[k]=exp[i];
k++;
i++;
}
Temp[k]='\0';
j=0;
for ( l=0; lSVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 13


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao




GVHD: Phan Thanh

{
if ( temp[l]=='.')j++;
if ( j>1) return 0;
}
val=atof(temp);
val1=ceil(val);
if(val1!=val)
{
while ( exp[i]==' ' && iif( exp[i]=='%'||exp[i]=='!') return 0;
l=i-k-1;
while ( exp[l]==' ' && l>=0) l--;
if ( exp[l]=='%') return 0;
}
while ( exp[i]==' ' && iif (( test(exp[i])==3)|| exp[i]=='(') return 0;
}
else if ( test(exp[i])==3)
{
k=0;
while ( test(exp[i])==3)
{
temp[k]=exp[i];
k++;
i++;
}
temp[k]='\0';

if ( strcmpi(temp,"pi") ==0 )
{
while ( exp[i]==' ' && iif (( test(exp[i])==1)||( test(exp[i])==3)|| exp[i]=='(') return 0;
if ( i==m) return 1;
}
else if ( strcmpi(temp,"sin") ==0 ) ok=0;
else if ( strcmpi(temp,"cos") ==0 ) ok=0;
else if ( strcmpi(temp,"tg") ==0 ) ok=0;
else if ( strcmpi(temp,"cotg")==0 ) ok=0;
else if ( strcmpi(temp,"ln") ==0 ) ok=0;
else if ( strcmpi(temp,"lg") ==0 ) ok=0;
else if ( strcmpi(temp,"sqrt")==0 ) ok=0;
else if ( strcmpi(temp,"exp") ==0 ) ok=0;
else if ( strcmpi(temp,"asin")==0 ) ok=0;
else if ( strcmpi(temp,"acos")==0 ) ok=0;
else if ( strcmpi(temp,"atan")==0 ) ok=0;
else
{
ok=1;
if ( ok==1 ) return 0;
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 14


ĐỒ ÁN CẤU TRÚC DỮ LIỆU

Tao



GVHD: Phan Thanh

while ( exp[i]==' ' && iif ( test(exp[i])==2 && exp[i]!='+' && exp[i]!= '-') return 0;
if ( i==m) return 0;
}
}
else if ( test(exp[i])==2)
{
for ( l=0; lif ( z==0) return 0;
k=0;
while (( test(exp[i])==2) && i{
temp[k]=exp[i];
k++;
i++;
}
k--;
l=0;
while ( exp[i]==' ' && iif ( k<1 && temp[k]=='!'&& (test(exp[i])==1||test(exp[i])==3||exp[i]=='('))
return 0;
if ( k==1 && temp[0]!='!' && temp[1]!='+' && temp[1]!='-') return 0;
if ( k==1 && (temp[0]=='%'&&temp[1]!='-'&&temp[1]!='+'))return 0;
if ( k==2 && temp[2]!='+'&& temp[2]!='-') return 0;

if ( k>2) return 0;
if ( exp[i-l-1]!='!'&& exp[i]==')') return 0;
if( i==m && exp[i-1-l]!='!') return 0;
}
else if ( exp[i]=='(')
{
if ( exp[i]=='(') n++;
i++;
while ( exp[i]==' ' && iif ( exp[i]==')') return 0;
if ( i==m) return 0;
if ( test(exp[i])==2 && exp[i]!='+' && exp[i]!='-') return 0;
}
else if ( exp[i]==')')
{
if (exp[i]==')'&& i==0) return 0;
else n--;
i++;
while ( exp[i]==' ' && i}
}
if ( n!=0) return 0;
return 1;
}
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 15



ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

+ Các hàm sau đây thực hiện nhiệm vụ tính toán một biểu thức:
Quá trình tính toán giá trị của biểu thức hậu tố khá tự nhiên đối với máy tính. Ý
tưởng là đọc biểu thức từ trái sang phải, nếu gặp một toán hạng (con số hoặc biến)
thì push toán hạng này vào ngăn xếp; nếu gặp toán tử, lấy hai toán hạng ra khỏi
ngăn xếp (stack), tính kết quả, đẩy kết quả trở lại ngăn xếp. Khi quá trình kết thúc
thì con số cuối cùng còn lại trong ngăn xếp chính là giá trị của biểu thức đó.
*
Xây dựng ngăn xếp tính kết quả từ biểu thức hậu tố nêu trên.
struct tinh
{
double x;
tinh *next;
};
tinh *T;
long giaithua(int n)
{
if(n==0||n==1) return 1;
else return n*giaithua(n-1);
}

*


Hàm thực hiện việc đẩy phần tử vào trong stack T

void push_T(double x)
{
tinh *new_ele;
new_ele=new(tinh);
new_ele->x=x;
new_ele->next=NULL;
if(T==NULL) T=new_ele;
else
{
new_ele->next=T;
T=new_ele;
}
return;
}

*

Hàm đọc ra một phần tử trên đỉnh stack T:

double pop_T()
{
tinh *cur;
double x;
if(T==NULL) return 0;
else
{
cur=T;

x=T->x;
T=T->next;
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 16


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

delete(cur);
}
return x;
}

*
Hàm thực hiện tính toán và trong hàm này cũng kèm theo việc kiểm tra
biểu thức tránh trường hợp suy biến do tính toán:
void calculate()
{
double x1,x2,s;
int ok=1;
hauto *cur;

T=NULL;
cur=P;
while(cur!=NULL)
{
if ( strcmp(cur->letter,"#")==0)
{
x1=pop_T();
cur=cur->next;
if ( strcmp(cur->letter,"-")==0)
{
s=0-x1;
push_T(s);
}
else if (strcmp(cur->letter,"+")==0)
{
push_T(x1);
}
}
else if(strcmp(cur->letter,"+")==0)
{
x2=pop_T();
x1=pop_T();
s=x1+x2;
push_T(s);
}
else if(strcmp(cur->letter,"-")==0)
{
x2=pop_T();
x1=pop_T();
s=x1-x2;

push_T(s);
}
else if(strcmp(cur->letter,"*")==0)
{
x2=pop_T();
x1=pop_T();
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 17


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

s=x1*x2;
push_T(s);
}
else if(strcmp(cur->letter,"/")==0)
{
x2=pop_T();
if(x2!=0)
{
x1=pop_T();

s=x1/x2;
push_T(s);
}
else
{
ok=0;
break;
}
}
else if(strcmp(cur->letter,"%")==0)
{
x2=pop_T();
if ( x2==0)
{
ok=0;
break;
}
else
{
x1=pop_T();
s=(int)x1%(int)x2;
push_T(s);
}
}
else if(strcmp(cur->letter,"^")==0)
{
x2=pop_T();
x1=pop_T();
if ( x2==0&&x1==0)
{

ok=0;
break;
}
else
{
s=pow(x1,x2);
push_T(s);
}
}
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 18


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

else if(strcmp(cur->letter,"!")==0)
{
x1=pop_T();
if(x1<0)
{
ok=0;

break;
}
else
{
s=giaithua((int)x1);
push_T(s);
}
}
else if(strcmp(cur->letter,"sin")==0)
{
x1=pop_T();
s=sin(x1);
push_T(s);
}
else if(strcmp(cur->letter,"cos")==0)
{
x1=pop_T();
s=cos(x1);
push_T(s);
}
else if(strcmp(cur->letter,"tg")==0)
{
x1=pop_T();
if ( cos (x1) ==0 )
{
ok=0;
break;
}
else
{

s=tan(x1);
push_T(s);
}
}
else if(strcmp(cur->letter,"cotg")==0)
{
x1=pop_T();
if ( sin (x1) ==0)
{
ok=0;
break;
}
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 19


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

else
{
s=1.0/tan(x1);

push_T(s);
}
}
else if(strcmp(cur->letter,"ln")==0)
{
x1=pop_T();
if( x1 >0)
{
s=log(x1);
push_T(s);
}
else
{
ok=0;
break;
}
}
else if(strcmp(cur->letter,"lg")==0)
{
x1=pop_T();
if( x1 >0)
{
s=log10(x1);
push_T(s);
}
else
{
ok=0;
break;
}

}
else if(strcmp(cur->letter,"sqrt")==0)
{
x1=pop_T();
if ( x1 <0 )
{
ok=0;
break;
}
else
{
s=sqrt(x1);
push_T(s);
}
}
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 20


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh


else if(strcmp(cur->letter,"exp")==0)
{
x1=pop_T();
s=exp(x1);
push_T(s);
}
else if(strcmp(cur->letter,"asin")==0)
{
x1=pop_T();
s=asin(x1);
push_T(s);
}
else if(strcmp(cur->letter,"acos")==0)
{
x1=pop_T();
s=acos(x1);
push_T(s);
}
else if(strcmp(cur->letter,"atan")==0)
{
x1=pop_T();
s=atan(x1);
push_T(s);
}
else push_T(cur->value);
cur=cur->next;
}
textcolor(3);
if (ok) cprintf("%.3lf",pop_T());
else cprintf(" Bieu thuc sai( kq)..");

return;
}
Đề tài này có thể mở rộng hơn nữa trong việc xử lý biến. Ở đây chúng tôi xin nêu ra
hướng giải quyết đối với trường hợp này thôi.
Khi một dãy kí tự vừa số mà không phải các số, toán tử, cũng không phải hàm , hay
hằng số (pi) thì ta thực hiện việc kiểm tra một kí tự tiếp theo có phải là kí tự ‘=’ không, nếu
đúng ta thực hiện lưu giá trị này vào một danh sách liên kết riêng B có kiểu cấu trúc gồm 3
trường: name, value, next và khi gặp lại biến này nó thực hiện việc lấy lại giá trị trong danh
sách liên kết B. Khi xét một xâu vào thì chúng ta có thể xảy ra biến đã được khai báo hoặc
chưa được khai báo, tuỳ trường hợp mà ta đưa ra các thông báo khác nhau cho người
dùng.

V. Viết chương trình:
#include <stdio.h>
#include <conio.h>
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 21


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh


#include <stdlib.h>
#include <string.h>
#include <math.h>
#define Left 10
#define Top 10
#define Row 5
#define Column 6
#define mau_khung 10
#define mau_phim12
#define BackGround 9
/*-------------------------------------------------------------------*/
struct toantu
{
char tt;
toantu *next;
};
struct hauto
{
char letter[10];
double value;
hauto *next;
};
/*-------------------------------------------------------------------*/
struct tinh
{
double x;
tinh *next;
};
/*-------------------------------------------------------------------*/
typedef toantu *stack;

typedef hauto *list;
stack S;
list P;
tinh *T;
/*-------------------------------------------------------------------*/
char* btnControl[5][6]={
{" Sin "," Cos "," Tg "," Cotg"," Lg "," Ln "},
{" asin"," acos"," atan"," Sqrt"," ( ) "," . "},
{" Exp "," x^y "," % "," 7 "," 8 "," 9 "},
{" ! "," + "," - "," 6 "," 5 "," 4 "},
{" / "," * "," 0 "," 1 "," 2 "," 3 "}
};
/*-------------------------------------------------------------------*/
void Khung ( int left , int top , int width, int height, int color );
void Khung_1( int left , int top , int width, int height, int color );
void write_string ( char *str, int x, int y, int color);
void keyboard();
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 22


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao




GVHD: Phan Thanh

void gioithieu();
void create();
int empty();
void push(char x);
char pop();
char top();
int test_am ( char *expr,int i);
void insert_last(char x,double val);
int ref(char x);
int test(char x);
void convert(char *expr);
void display();
void calculate();
double pop_T();
void push_T(double x);
long giaithua(int n);
int menu();
void menu_list(char x);
int test_bt(char *exp);
/*-------------------------------------------------------------------*/
main()
{
textbackground(1);
char expr[50];
int key;
gioithieu();
do {
clrscr();

keyboard();
fflush(stdin);
textcolor(3);
gets(expr);
if ( strlen(expr)&&(strcmpi(expr, "exit")!=0))
if (test_bt(expr))
{
convert(expr);
gotoxy(Left+1,Top-2);textcolor(3);
display();
gotoxy(Left+Column*5+3,Top-5);textcolor(3);
calculate();
}
else
{
write_string ("bieu thuc sai ( do nhap)...",Left+1,Top-5,3);
}
else
return 0;
getch();
}while(1);
return 1;
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06



Trang: 23



ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

}
/*-------------------------------------------------------------------*/
void Khung ( int left , int top , int width, int height, int color )
{
int i;
textcolor(color);
gotoxy(left, top);
putch(201);
gotoxy(left, top+height+1);
putch(200);
gotoxy(left+width+1, top);
putch(187);
gotoxy(left+width+1, top+height+1);
putch(188);
for (i=1;i <=width; i++)
{
gotoxy(left+i, top);
putch(205);
gotoxy(left+i, top+height+1);
putch(205);
}
for ( i=1; i<=height ; i++)
{

gotoxy( left, top+i);
putch(186);
gotoxy( left+width+1, top+i);
putch(186);
}
return ;
}
/*-------------------------------------------------------------------*/
void write_string ( char *str, int x, int y, int color)
{
textcolor(color);
gotoxy(x, y);
cprintf(str);
return;
}
/*-------------------------------------------------------------------*/
void gioithieu()
{
clrscr();
Khung ( 20, 3 , 40 , 2, 2);
Khung ( 20, 9 , 40 , 3, 2);
Khung ( 20, 16 , 40 , 5, 2);
write_string(" DO AN CAU TRUC DU LIEU & THUAT TOAN ", 23,4 , 11);
write_string(" De tai 07: ",23, 10 ,11);
write_string(" Chuyen bieu thuc trung to sang hau to ",22, 12 ,11);
SVTH: Trịnh Việt Hùng
Lớp 09T2 - Nhóm: 06




Trang: 24


ĐỒ ÁN CẤU TRÚC DỮ LIỆU
Tao



GVHD: Phan Thanh

write_string(" GVHD : Phan Thanh Tao",23, 17 ,11);
write_string(" NHOM : 09NH06",23, 18 ,11);
write_string(" LOP
: 09T2",23, 19 ,11);
write_string(" HO TEN SV: Trinh Viet Hung",23, 20 ,11);
textcolor(7);
gotoxy(10,24);
cprintf("\n Ban hay nhan mot phim bat ki.......");
getch();
return;
}
/*-------------------------------------------------------------------*/
void Khung_1(int left, int top, int width, int height, int color)
{
textcolor(color);
gotoxy(left,top);
putch(218);
gotoxy(left,top+height+1);
putch(192);
gotoxy(left+width+1,top);

putch(191);
gotoxy(left+width+1,top+height+1);
putch(217);
for(int i=1;i<=width;i++)
{
gotoxy(left+i,top);
putch(196);
gotoxy(left+i,top+height+1);
putch(196);
}
for(int j=1;j<=height;j++)
{
gotoxy(left,top+j);
putch(179);
gotoxy(left+width+1,top+j);
putch(179);
}
return;
}
/*-------------------------------------------------------------------*/
void keyboard()
{
Khung_1(Left-1,Top-8,Column*6+6,Row*4+2,RED);
write_string("My Calculator !",Left+6,Top-7,14);
Khung_1(Left,Top-6,Column*5-1,1,mau_khung);cprintf("=");
Khung_1(Left+Column*5+2,Top-6,8,1,mau_khung);
Khung_1(Left,Top-3,Column*6+4,1,mau_khung);
textbackground(BackGround);
write_string("Note:",Left+Column*7+2,Row*4,14);
SVTH: Trịnh Việt Hùng

Lớp 09T2 - Nhóm: 06



Trang: 25


×