Chương 7 Các lệnh có cấu trúc

Cấu trúc tuần tự Cấu trúc lựa chọn Cấu trúc lặp Giới thiệu thuật toán đệ quy Ngoại lệ và xử lý ngoại lệ

pdf42 trang | Chia sẻ: lylyngoc | Lượt xem: 1649 | Lượt tải: 3download
Bạn đang xem trước 20 trang tài liệu Chương 7 Các lệnh có cấu trúc, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Chương 7 CÁC LỆNH CÓ CẤU TRÚC Lý thuyết ngôn ngữ lập trình Nội dung Cấu trúc tuần tự Cấu trúc lựa chọn Cấu trúc lặp Giới thiệu thuật toán đệ quy Ngoại lệ và xử lý ngoại lệ Cấu trúc tuần tự Các công việc (các thao tác) được thực hiện một cách tuần tự, công việc này nối tiếp công việc kia Lưu đồ thuật toán tổng quát của cấu trúc tuần tự : Lệnh 1 Lệnh 2 … Lệnh n Cấu trúc tuần tự (tt) Một chương trình C sử dụng cấu trúc tuần tự. int main() { int a=5, b=6,c; float x=3.5, y=4.5,z; printf(“Day la chuong trinh chinh”); c= a + b; printf(“\n Tong cua %d va %d la %d”,a,b,c); z= x + y; printf(“\n Tong cua %f và %f là %f”, x,y,z); getch(); return 0; } Cấu trúc lựa chọn Lựa chọn một công việc để thực hiện căn cứ vào một điều kiện cho trước nào đó. Có một số dạng cấu trúc lựa chọn thông dụng như sau: Cấu trúc 1: Nếu (đúng) thì thực hiện . Trong ngôn ngữ lập trình C, cấu trúc này được thể hiện bằng lệnh if có cú pháp như sau : if () Cấu trúc lựa chọn (tt) Ví dụ : #include #include void main() { int a,b,c; print ( "\ nhập 2 số a,b : "); scanf(" %f %f ", &a, &b); if (a>b) c =a-b; printf (“c = %f \n”,c) } Cấu trúc lựa chọn (tt) Cấu trúc 2: Nếu (đúng) thì thực hiện <công việc 1>, ngược lại (điều kiện sai) thực hiện . Trong ngôn ngữ lập trình C, cấu trúc này được thể hiện bằng lệnh if ..else có cú pháp như sau : if () else Cấu trúc lựa chọn (tt) Ví dụ : #include #include void main() { int a,b,c; print ( "\ nhập 2 số a,b : "); scanf(" %f %f ", &a, &b); if (a>b) c =a-b; else c = b-a; printf (“c = %f \n”,c) } Cấu trúc lựa chọn (tt) chúng ta có thể sử dụng nhiều lệnh if .. else lồng nhau. Ví dụ : Chương trình C giải phương trình bậc nhất. #Include #Include void main ( void) {float a, b ; print ( "\ nhập 2 số a,b : "); scanf(" %f %f ", &a, &b); if ( a= = 0 ) if( b= =0 ) Printf (" Phương trình có vô số nghiệm ! \n " ); else Printf (" phương trình vô nghiệm \n "); else / * a khác 0 */ Printf (" phương trình có nghiệm là : x= %f \n ", -b/a); Printf( " ấn phím bất kỳ tiếp tục "); getch(); } Cấu trúc lựa chọn (tt) Cấu trúc 3: Trường hợp thực hiện . Trong ngôn ngữ lập trình C, cấu trúc này được thể hiện bằng lệnh switch có cú pháp như sau: switch () { case : // nếu = <biểu thức so sánh 1> ; break; case : // nếu = <biểu thức so sánh 2> ; break; … case : // nếu = <biểu thức so sánh n> ; break; default : // nếu không xảy ra tất cả các trường hợp trên } Cấu trúc lựa chọn (tt) Ví dụ : #Include #Include main( ) { Int so ; printf(" \n Nhập vào một số "); scanf(" %d ", &so); switch(so) { case 0 : printf(" \n Đây là số KHÔNG "); break; case 1 : printf( " \n Đây là số MỘT"); break; case 2 : printf(" \n Đây là số HAI");break; case 3 : printf (" \n Đây là số BA ");break; default : printf( " \n Đây là số từ BỐN trở lên hoặc là ký tự"); } } Cấu trúc lặp Thực hiện lặp đi lặp lại một công việc nhiều lần căn cứ vào một điều kiện nào đó. Cấu trúc này có hai dạng thông dụng như sau: Lặp xác định: Người lập trình đã xác định trước được sẽ lặp đi lặp lại công việc bao nhiêu lần khi viết chương trình. Cấu trúc lặp (tt) Trong ngôn ngữ lập trình C, cấu trúc này được thể hiện bằng vòng lặp for có cú pháp như sau: for (biểu thức 1; biểu thức 2;biểu thức 3) Trong đó: biểu thức1 thông thường là một phép gán để khởi tạo giá trị ban đầu cho biến điều khiển. biểu thức2 là một biểu thức kiểm tra điều kiện đúng sai để thoát khỏi vòng lặp. biểu thức3 thông thường là một phép gán để thay đổi biến điều khiển có thể là lệnh rỗng, một câu lệnh hoặc một khối lệnh. Cấu trúc lặp (tt) Ví dụ: Chương trình tìm phương án đổi tiền. #include #define TONGSOTIEN 300000 void main() { long i, j, k, l, m, count=0, soluong = 0; for (i=0; i<=TONGSOTIEN/1000; i++) for (j=0; j<=TONGSOTIEN/2000; j++) for (k=0; k<=TONGSOTIEN/5000; k++) for (l=0; l<=TONGSOTIEN/10000; l++) for (m=0; m<=TONGSOTIEN/20000; m++) Cấu trúc lặp (tt) {if ((i*1000 + j*2000 + k*5000 + l*10000 + m*20000) == TONGSOTIEN) printf("\n%5ld - %5ld%5ld%5ld%5ld%5ld", ++count, i, j, k, l, m); soluong++; } printf("so luong = %ld", soluong); getch(); } Cấu trúc lặp (tt) Lặp không xác định: Người lập trình không xác định được sẽ lặp đi lặp lại công việc bao nhiêu lần, mà số lần lặp đó chỉ xác định được khi thực thi chương trình. Cấu trúc lặp (tt) Trong ngôn ngữ lập trình C, cấu trúc này được thể hiện bằng vòng lặp while và vòng lặp do .. while có cú pháp như sau: + Vòng lặp while while () Trong đó biểu thức là một biểu thức kiểm tra điều kiện đúng sai để thoát khỏi vòng lặp và các lệnh có thể là lệnh rỗng, một câu lệnh hay khối lệnh Cấu trúc lặp (tt) Ví dụ : Chương trình in dãy số Fibonaci #include void main() { int n, i, fib1 = 1, fib2 = 1, fib = 2; printf("\nNhap gia tri N : "); scanf("%d", &n); printf("%d %d ", fib1, fib2); while (fib1+fib2 < n) { fib = fib1 + fib2; printf("%d ", fib); fib2 = fib1; fib1 = fib; } getch(); } Cấu trúc lặp (tt) + Vòng lặp do .. while: do while Tương tự như vòng lặp while, nhưng có điểm khác là các lệnh được thực hiện ít nhất một lần vì việc kiểm tra biểu thức điều kiện được thực hiện sau. Cấu trúc lặp (tt) Ví dụ : Chương trình cho biết mã ASCII của phím bất kỳ #include void main() { char c; printf("\nNhap vao mot phim muon biet ma cua no. ESC de thoat\n"); Cấu trúc lặp (tt) do { c = getch(); if (c == 0) { c = getch(); printf("\nMa mo rong : %d", c); } else printf("\nMa thuong : %d", c); } while (c != 27); } Giới thiệu thuật toán đệ quy Như đã biết, một thuật toán cần phải thỏa mãn ít nhất 3 tính chất : - Tính hữu hạn. - Tính xác định - Tính đúng đắn Tuy nhiên, có những bài toán mà việc xây dựng một thuật toán với đầy đủ ba tính chất trên rất khó khăn. Trong khi đó, nếu ta xây dựng một thuật toán vi phạm một vài tính chất trên thì cách giải lại trở nên đơn giản hơn nhiều và có thể chấp nhận được. Một trong những trường hợp đó là thuật toán đệ quy. Giới thiệu thuật toán đệ quy (tt) Tư tưởng giải bài toán bằng thuật toán đệ quy là đưa bài toán hiện tại về một bài toán cùng loại, cùng tính chất (đồng dạng) nhưng ở cấp độ thấp hơn (chẳng hạn : độ lớn dữ liệu nhập nhỏ hơn, giá trị cần tính toán nhỏ hơn, ....), và quá trình này tiếp tục cho đến lúc bài toán được đưa về một cấp độ mà tại đó có thể giải được. Từ kết quả ở cấp độ này, ta sẽ lần ngược để giải được bài toán ở cấp độ cao hơn cho đến lúc giải được bài toán ở cấp độ ban đầu. Trong toán học ta cũng thường gặp những định nghĩa về những đối tượng, những khái niệm dựa trên chính những đối tượng, khái niệm đó. Giới thiệu thuật toán đệ quy (tt) - Ðịnh nghĩa giai thừa Giai thừa của một số tự nhiên n, ký hiệu n! được định nghĩa là : 0! = 1 n! = (n-1)!n với mọi n>0 - Ðịnh nghĩa dãy số Fibonacci f0 = 1 f1 = 1 fn = fn-1 + fn-2 với mọi n>1 Theo toán học, những khái niệm được định nghĩa như vậy gọi là định nghĩa theo kiểu quy nạp. Chính vì vậy, đệ quy có sự liên hệ rất chặt chẽ với quy nạp toán học. Giới thiệu thuật toán đệ quy (tt) Ðệ quy mạnh ở điểm nó có thể định nghĩa một tập vô hạn các đối tượng chỉ bằng một số hữu hạn các mệnh đề. Tuy nhiên, đặc tính này của đệ quy lại vi phạm tính xác định của thuật toán. Về nguyên tắc, một bước trong thuật toán phải được xác định ngay tại thời điểm bước đó được thi hành, nhưng với thuật toán đệ quy, bước thứ n không được xác định ngay trong ngữ cảnh của nó mà phải xác định thông qua một bước thấp hơn. Giới thiệu thuật toán đệ quy (tt) Ưu thế của thuật toán đệ quy là ta chỉ cần giải bài toán tại một số trường hợp đặc biệt nào đó, còn gọi là trường hợp dừng. Sau đó, các trường hợp khác của bài toán sẽ được xác định thông qua trường hợp đặc biệt này. Ðối với việc tính dãy Fibonacci, trường hợp dừng chính là giá trị của f0 và f1. Giới thiệu thuật toán đệ quy (tt) Mọi thuật toán đệ quy đều gồm hai phần: - Phần cơ sở Là các trường hợp không cần thực hiện lại thuật toán (hay không có yêu cầu gọi đệ quy). Nếu thuật toán đệ quy không có phần này thì sẽ dẫn đến bị lặp vô hạn và sinh lỗi khi thi hành. Vì lý do này mà người ta đôi lúc còn gọi phần cơ sở là trường hợp dừng. - Phần đệ quy Là phần trong thuật toán có yêu cầu gọi đệ quy, tức là yêu cầu thực hiện lại thuật toán nhưng với một cấp độ dữ liệu thấp hơn. Giới thiệu thuật toán đệ quy (tt) - Chương trình không dùng thuật toán đệ quy được viết như sau: #include unsigned long giaithua(int n) { unsigned long ketqua = 1; int i; for (i=2; i<=n; i++) ketqua *= i; return ketqua; } Giới thiệu thuật toán đệ quy (tt) void main() { int n; printf("\nNhap vao gia tri N : "); scanf("%d", &n); printf("%d! = %lu", n, giaithua(n)); getch(); } Giới thiệu thuật toán đệ quy (tt) - Chương trình dùng thuật toán đệ quy được viết lại như sau: #include unsigned long giaithua(int n) { if (n==0||n==1) return 1; else return n*giaithua(n-1); } Giới thiệu thuật toán đệ quy (tt) void main() { int n; printf("\nNhap vao gia tri N : "); scanf("%d", &n); printf("%d! = %lu", n, giaithua(n)); getch(); } Ngoại lệ và xử lý ngoại lệ Khái niệm Trong quá trình thực hiện chương trình thường xảy ra một số sự kiện đặc biệt hoặc các lỗi như sự tràn số, truy xuất đến chỉ số mảng nằm ngoài tập chỉ số, thực hiện lệnh đọc một phần tử cuối tập tin ... Các sự kiện đó được gọi là ngoại lệ (exception). Thay vì tiếp tục thực hiện chương trình bình thường, một chương trình con sẽ được gọi để thực hiện một vài xử lý đặc biệt nào đó gọi là xử lý ngoại lệ. Hành động chú ý đến ngoại lệ, ngắt sự thực hiện chương trình và chuyển điều khiển đến xử lý ngoại lệ được gọi là đề xuất ngoại lệ (raising the exception). Ngoại lệ và xử lý ngoại lệ (tt) Xử lý ngoại lệ Thông thường các ngoại lệ đã được định nghĩa trước bởi ngôn ngữ, chẳng hạn như ZERO_DIVIDE chỉ sự kiện chia cho một số không, END_OF_FILE: hết tập tin , OVERFLOW: tràn số, hay tràn stack ... Xử lý ngoại lệ là một hành vi xử lý tương ứng khi một ngoại lệ có thể diễn ra. Ví dụ Ngoại lệ và xử lý ngoại lệ (tt) void example () { average = sum/total; ... return ; when zero_divide { average = 0; printf(“ error: cannot compute average, total is zero\n”); } ...... } /** function example **/ Ngoại lệ và xử lý ngoại lệ (tt) Sau khi đã hoàn thành việc xử lý một ngoại lệ và xử lý đó đã kết thúc thì có một vấn đề đặt ra là quyền điều khiển được chuyển tới chỗ nào? - Ðiều khiển nên được chuyển tới chỗ mà ngoại lệ được đề xuất? - Ðiều khiển nên chuyển về lệnh trong chương trình con chứa xử lý nơi mà ngoại lệ được đề xuất sau khi được truyền tới?Chương trình con chứa xử lý tự kết thúc một cách bình thường và nó xuất hiện tại chương trình gọi như là không có gì xẩy ra. - Ðây là những lựa chọn khi thiết kế ngôn ngữ. Ngoại lệ và xử lý ngoại lệ (tt) Đề xuất ngoại lệ Một ngoại lệ có thể bị đề xuất bằng phép toán nguyên thuỷ được định nghĩa bởi ngôn ngữ chẳng hạn phép cộng, phép nhân có thể đề xuất ngoại lệ OVERFLOW. Ngoài ra, một ngoại lệ có thể bị đề xuất một cách tường minh bởi người lập trình bằng cách dùng một lệnh được cung cấp cho mục đích đó. Lệnh này có thể được thực hiện trong một chương trình con sau khi xác định một biến riêng hoặc tập tin nhập chứa giá trị không đúng. Ngoại lệ và xử lý ngoại lệ (tt) Lan truyền ngoại lệ Thông thường, vị trí mà một ngoại lệ xuất hiện không phải là vị trí tốt nhất để xử lý nó. Khi một ngoại lệ được xử lý trong một chương trình con khác chứ không phải trong chương trình con mà nó được đề xuất thì ngoại lệ đó được gọi là được truyền (propagated) từ điểm mà tại đó nó được đề xuất đến điểm mà nó được xử lý. Ngoại lệ và xử lý ngoại lệ (tt) Quy tắc để xác định việc xử lý một ngoại lệ đặc thù thường được gọi là chuỗi động (dynamic chain) của các kích hoạt chương trình con hướng tới chương trình con mà nó đề xuất ngoại lệ. Ngoại lệ và xử lý ngoại lệ (tt) Khi một ngoại lệ P được đề xuất trong chương trình con C, thì P được xử lý bởi một xử lý được định nghĩa trong C nếu có một xử lý như thế. Nếu không có thì C kết thúc. Nếu chương trình con B gọi C thì ngoại lệ được truyền đến B và một lần nữa được đề xuất tại điểm trong B nơi mà B gọi C. Nếu B không cung cấp một xử lý cho P thì B bị kết thúc và ngoại lệ lại được truyền tới chương trình gọi B... Nếu các chương trình con và bản thân chương trình chính không có xử lý cho P thì toàn bộ chương trình kết thúc và xử lý chuẩn được định nghĩa bởi ngôn ngữ sẽ được gọi tới. Ngoại lệ và xử lý ngoại lệ (tt) Một hiệu quả quan trọng của quy tắc này đối với việc truyền các ngoại lệ là nó cho phép một chương trình con kế thừa (remain) như là một phép toán trừu tượng được định nghĩa bởi người lập trình ngay cả trong việc xử lý ngoại lệ. Ngoại lệ và xử lý ngoại lệ (tt) Một phép toán nguyên thuỷ có thể bất ngờ ngắt quá trình bình thường của nó và đề xuất một ngoại lệ. Tương tự, thông qua việc thực hiện lệnh RAISE, một chương trình con có thể bất ngờ ngắt quá trình bình thường của nó và đề xuất một ngoại lệ. Ðến chương trình gọi thì hiệu quả của đề xuất ngoại lệ của chương trình con cũng giống như hiệu quả đề xuất của phép toán nguyên thủy, nếu chương trình con tự nó không có một xử lý ngoại lệ. Nếu ngoại lệ được xử lý trong chương trình con thì chương trình con trở về một cách bình thường và chương trình gọi nó không bao giờ biết được rằng một ngoại lệ đã được đề xuất. CÂU HỎI ÔN TẬP CHƯƠNG 7 1. Khái niệm thuật toán đệ quy, ưu điểm và nhược điểm của đệ quy. 2. Viết chương trình để thực hiện các công việc sau: - In dãy số nguyên từ 1 đến 100 - Nhập vào một số nguyên n, tính tổng các số nguyên từ 1 đến n. - In ra dãy số 100 95 90 85 … 15 10 5 ra mà hình - Nhập vào một số nguyên n, tính tích các số lẻ từ 1 đến n. 3. Viết chương trình sử dụng thuật toán đệ quy in ra dãy số Fibonaci.