Bài giảng Kỹ thuật lập trình - Chương 6: Hàm (Funtion) - Trần Thị Kim Chi

1 Khái niệm và cú pháp 2 Tầm vực 3 Tham số và lời gọi hàm 4 Đệ quy Đặt vấn đề Viết chương trình tính S = a! + b! + c! với a, b, c là 3 số nguyên dương nhập từ bàn phím.

pdf74 trang | Chia sẻ: candy98 | Lượt xem: 619 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Bài giảng Kỹ thuật lập trình - Chương 6: Hàm (Funtion) - Trần Thị Kim Chi, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
VC & BB 1 Nội dung NMLT - Hàm (Function) Khái niệm và cú pháp 1 Tầm vực 2 Tham số và lời gọi hàm 3 Đệ quy 4 VC & BB 2 Đặt vấn đề Viết chương trình tính S = a! + b! + c! với a, b, c là 3 số nguyên dương nhập từ bàn phím. NMLT - Hàm (Function) Chương trình chính Nhập a, b, c > 0 Tính S = a! + b! + c! Xuất kết quả S Nhập a > 0 Nhập b > 0 Nhập c > 0 Tính s1=a! Tính s2=b! Tính s3=c! VC & BB 3 Đặt vấn đề 3 đoạn lệnh nhập a, b, c > 0 NMLT - Hàm (Function) do { cout<<“Nhap mot so nguyen duong: ”; cin>>a; } while (a <= 0); do { cout<<“Nhap mot so nguyen duong: ”; cin>>b; } while (b <= 0); do { cout<<“Nhap mot so nguyen duong: ”; cin>>c; } while (c <= 0); VC & BB 4 Đặt vấn đề 3 đoạn lệnh tính s1 = a!, s2 = b!, s3 = c! NMLT - Hàm (Function) { Tính s1 = a! = 1 * 2 * * a } s1 = 1; for (i = 2; i <= a ; i++) s1 = s1 * i; { Tính s2 = b! = 1 * 2 * * b } s2 = 1; for (i = 2; i <= b ; i++) s2 = s2 * i; { Tính s3 = c! = 1 * 2 * * c } s3 = 1; for (i = 2; i <= c ; i++) s3 = s3 * i; VC & BB 5 Đặt vấn đề Giải pháp => Viết 1 lần và sử dụng nhiều lần  Đoạn lệnh nhập tổng quát, với n = a, b, c  Đoạn lệnh tính giai thừa tổng quát, n = a, b, c NMLT - Hàm (Function) do { cout<<“Nhap mot so nguyen duong: ”; cin>>n; } while (n <= 0); { Tính s = n! = 1 * 2 * * n } s = 1; for (i = 2; i <= n ; i++) s = s * i; VC & BB 6 Hàm Khái niệm  Một đoạn chương trình có tên, đầu vào và đầu ra.  Có chức năng giải quyết một số vấn đề chuyên biệt cho chương trình chính.  Được gọi nhiều lần với các tham số khác nhau.  Được sử dụng khi có nhu cầu: • Tái sử dụng. • Sửa lỗi và cải tiến. NMLT - Hàm (Function) VC & BB 7 Hàm Các đặc trưng của Hàm Nằm trong hoặc ngoài văn bản có chương trình gọi đến hàm. Một văn bản có thể chứa nhiều hàm. Được gọi từ chương trình chính (main), từ hàm khác hoặc từ chính nó (đệ quy). Không lồng nhau. Có 3 cách truyền giá trị: Truyền theo tham trị, tham biến và tham trỏ. Các biến cục bộ trong hàm được tạo ra khi hàm được gọi và biến mất khi hàm thực thi xong. NMLT - Hàm (Function) VC & BB 8 Hàm NMLT - Hàm (Function) VC & BB 9 Hàm Có 2 loại hàm trong NNLT “C/C++”:  Hàm thư viện (library functions): Do chương trình dịch “C/C++” cung cấp. Để sử dụng các hàm này trong chương trình, đầu chương trình phải chứa các khai báo và định nghĩa hằng, biến, hàm nguyên mẫu, . . . bằng các chỉ thị tiền xử lý #include . Ví dụ: #include #include  Hàm tự tạo: Do người sử dụng định nghĩa thêm các hàm khác phục vụ cho nhu cầu lập trình của mình. NMLT - Hàm (Function) VC & BB 10 Hàm Cú pháp  Trong đó • : kiểu bất kỳ của C (char, int, long, float,). Nếu không trả về thì là void. • : theo quy tắc đặt tên định danh. • : tham số hình thức đầu vào giống khai báo biến, cách nhau bằng dấu , • : trả về cho hàm qua lệnh return. NMLT - Hàm (Function) ([danh sách tham số]) { [return ;] } VC & BB 11 Các bước viết hàm Cần xác định các thông tin sau đây:  Tên hàm.  Hàm sẽ thực hiện công việc gì.  Các đầu vào (nếu có).  Đầu ra (nếu có). NMLT - Hàm (Function) Tên hàm Đầu vào 1 Đầu vào 2 Đầu vào n Đầu ra (nếu có) Các công việc sẽ thực hiện VC & BB 12 Hàm Ví dụ 1  Tên hàm: XuatTong  Công việc: tính và xuất tổng 2 số nguyên  Đầu vào: hai số nguyên x và y  Đầu ra: không có NMLT - Hàm (Function) void XuatTong(int x, int y) { int s; s = x + y; cout<<x<<“+”<<y<<“=“<<s; } VC & BB 13 Hàm Ví dụ 2  Tên hàm: TinhTong  Công việc: tính và trả về tổng 2 số nguyên  Đầu vào: hai số nguyên x và y  Đầu ra: một số nguyên có giá trị x + y NMLT - Hàm (Function) int TinhTong(int x, int y) { int s; s = x + y; return s; } VC & BB 14 Chương trình con - Function Ví dụ 3  Tên hàm: NhapXuatTong  Công việc: nhập và xuất tổng 2 số nguyên  Đầu vào: không có  Đầu ra: không có NMLT - Hàm (Function) void NhapXuatTong() { int x, y; cout<<“Nhap 2 so nguyen: ”; cin>>x>>y; cout<<x<<“+”<<y<<“=“<<x + y); } VC & BB 15 Tầm vực Khái niệm  Là phạm vi hoạt động của biến và hàm.  Biến: • Toàn cục: khai báo ngoài tất cả các hàm (kể cả hàm main) và có tác dụng lên toàn bộ chương trình. • Cục bộ: khai báo trong hàm hoặc khối { } và chỉ có tác dụng trong bản thân hàm hoặc khối đó (kể cả khối con nó). Biến cục bộ sẽ bị xóa khỏi bộ nhớ khi kết thúc khối khai báo nó. NMLT - Hàm (Function) VC & BB 16 Tầm vực NMLT - Hàm (Function) int a; int Ham1() { int a1; } int Ham2() { int a2; { int a21; } } void main() { int a3; } VC & BB 17 Hàm nguyên mẫu (function prototype)  Hàm nguyên mẫu:  Được dùng để cung cấp thông tin cho chương trình dịch về tên hàm, kiểu giá trị trả về, số lượng, thứ tự và kiểu của các tham số của hàm.  Chương trình dịch căn cứ vào các thông tin này để kiểm tra các lời gọi hàm trong chương trình.  Hàm nguyên mẫu được đặt sau phần khai báo toàn cục và ngay trước hàm main() hoặc có thể đặt trong tập tin khác.  Khai báo: [] ([]) ;  Ví dụ: Khai báo hàm nguyên mẫu có chức năng xác định trị min giữa 2 số nguyên. int Min(int, int) ; int Min(int a, int b) ; // nên dùng cách khai báo này VC & BB 18 Tổ chức một chương trình “C/C++”  Cách 1: chương trình gồm 3 phần PHẦN KHAI BÁO TOÀN CỤC PHẦN KHAI BÁO VÀ ĐỊNH NGHĨA HÀM HÀM main()  Cách 2: chương trình gồm 4 phần (nên dùng cách này) PHẦN KHAI BÁO TOÀN CỤC PHẦN KHAI BÁO HÀM NGUYÊN MẪU HÀM main() PHẦN ĐỊNH NGHĨA HÀM VC & BB 19  Ví dụ: cách 1 #include int min(int a, int b) { if (a<b) return a; else return b; } void main() { int a=40, b=30; int min1 = min(a,b); cout << “Min = “ << min1; }  Ví dụ: Cách 2 #include int min(int a, int b); //prototype void main() { int a=40, b=30; int min1 = min(a,b); cout << “Min = “ << min1; } int min(int a, int b) { if (a<b) return a; else return b; } Tổ chức một chương trình “C/C++” VC & BB 20 Các phương pháp truyền tham số Có hai loại tham số:  Tham số thực (actual parameter):là tham số trong lời gọi hàm.  Tham số hình thức (formal parameter): là tham số trong phần khai báo và định nghĩa. Tham số hình thức chỉ là tên đại diện cho tham số thực tương ứng. Kiểu của tham số hình thức sẽ qui định kiểu của tham số thực. VC & BB 21 Các phương pháp truyền tham số  Ví dụ: int min(int a, int b) //a,b là tham số hình thức { if(a<b) return a; else return b; } void main() { int minAB =min(7,10)//Gọi hàm // a = 7, b=10 } // Lúc này a,b là tham số thực VC & BB 22 Các phương pháp truyền tham số Có hai cách truyền tham số: 1. Truyền tham trị (call by value):  Chương trình dịch cấp phát vùng nhớ riêng cho từng tham số hình thức, sau đó sao chép giá trị của tham số thực tương ứng vào các tham số hình thức.  Khi kết thúc thực hiện hàm, chương trình dịch sẽ thu hồi các vùng nhớ đã cấp phát cho các tham số hình thức, và các biến cục bộ khai báo bên trong hàm.  Như vậy, mọi sự thay đổi trị của các tham số hình thức đều không ảnh hưởng đến các tham số thực bên ngoài hàm.  Cách truyền: void F(int, int ); // truyền bằng trị hay void F(int a, int b); // truyền bằng trị VC & BB 23 Truyền Giá trị (Call by Value)  Truyền đối số cho hàm ở dạng giá trị.  Có thể truyền hằng, biến, biểu thức nhưng hàm chỉ sẽ nhận giá trị.  Được sử dụng khi không có nhu cầu thay đổi giá trị của tham số sau khi thực hiện hàm. NMLT - Hàm (Function) void TruyenGiaTri(int x) { x++; } Các phương pháp truyền tham số VC & BB 24 Các phương pháp truyền tham số  Ví dụ: Khảo sát chương trình sau #include void doubleNum(int a); //prototype void main() { int a=40; doubleNum(a); cout << “Inside main function:” << endl; cout << “a = “ << a << endl; } void doubleNum(int a) { a = a*2; cout << “Inside doubleNum function. a = “ << a; } VC & BB 25 Các phương pháp truyền tham số 2. Truyền tham chiếu(call by reference):  Chương trình dịch sẽ truyền địa chỉ của các tham số thực tương ứng cho các tham số hình thức.  Nghĩa là ta có thể xem tham số hình thức cũng chính là tham số thực, hay nói cách khác tham số hình thức là tên gọi khác của tham số thực.  Mọi sự thay đổi trị của tham số hình thức bên trong hàm chính là thay đổi trị của tham số thực bên ngoài hàm.  Cách truyền: void Swap(int &,int &); // truyền bằng tham chiếu hay void Swap(int & a,int & b); // truyền bằng tham chiếu VC & BB 26 Truyền Địa chỉ (Call by Address)  Truyền đối số cho hàm ở dạng địa chỉ (con trỏ).  Không được truyền giá trị cho tham số này.  Được sử dụng khi có nhu cầu thay đổi giá trị của tham số sau khi thực hiện hàm. NMLT - Hàm (Function) void TruyenDiaChi(int *x) { *x++; } Các phương pháp truyền tham số VC & BB 27 Truyền Tham chiếu (Call by Reference) (C++)  Truyền đối số cho hàm ở dạng địa chỉ (con trỏ). Được bắt đầu bằng & trong khai báo.  Không được truyền giá trị cho tham số này.  Được sử dụng khi có nhu cầu thay đổi giá trị của tham số sau khi thực hiện hàm. NMLT - Hàm (Function) void TruyenThamChieu(int &x) { x++; } Các phương pháp truyền tham số VC & BB 28 Các phương pháp truyền tham số  Ví dụ: Khảo sát chương trình sau #include void doubleNum(int a); //prototype void main() { int a=40; doubleNum(a); cout << “Inside main function:” << endl; cout << “a = “ << a << endl; } void doubleNum(int &a) { a = a*2; cout << “Inside doubleNum function. a = “ << a; } VC & BB 29 Các phương pháp truyền tham số Chú ý:  Trong cách truyền tham chiếu, tham số thực tương ứng phải là một biến. Còn trong cách truyền trị, tham số thực tương ứng có thể là biến, hằng, lời gọi hàm, hoặc một biểu thức cùng kiểu với tham số hình thức.  Các tham số hình thức trong cách truyền bằng giá trị được gọi là tham trị. Còn các tham số hình thức trong cách truyền bằng tham chiếu được gọi là tham biến. VC & BB 30 Lưu ý khi truyền đối số Lưu ý  Trong một hàm, các tham số có thể truyền theo nhiều cách. NMLT - Hàm (Function) void HonHop(int x, int &y) { x++; y++; } VC & BB 31 Lưu ý khi truyền đối số Lưu ý  Sử dụng tham chiếu là một cách để trả về giá trị cho chương trình. NMLT - Hàm (Function) int TinhTong(int x, int y) { return x + y; } void TinhTong(int x, int y, int &tong) { tong = x + y; } void TinhTongHieu(int x, int y, int &tong, int &hieu) { tong = x + y; hieu = x – y; } VC & BB 32 Lời gọi hàm Cách thực hiện  Gọi tên của hàm đồng thời truyền các đối số (hằng, biến, biểu thức) cho các tham số theo đúng thứ tự đã được khai báo trong hàm.  Các biến hoặc trị này cách nhau bằng dấu ,  Các đối số này được được đặt trong cặp dấu ngoặc đơn ( ) (, , ); NMLT - Hàm (Function) VC & BB 33 Lời gọi hàm Ví dụ NMLT - Hàm (Function) { Các hàm được khai báo ở đây } void main() { int n = 9; XuatTong(1, 2); XuatTong(1, n); TinhTong(1, 2); int tong = TinhTong(1, 2); TruyenGiaTri(1); TruyenGiaTri(n); TruyenDiaChi(1); TruyenDiaChi(&n); TruyenThamChieu(1); TruyenThamChieu(n); } VC & BB 34 Lời gọi chương trình con Ví dụ NMLT - Hàm (Function) void HoanVi(int &a, int &b); void main() { HoanVi(2912, 1706); int x = 2912, y = 1706; HoanVi(x, y); } void HoanVi(int &a, int &b) { int tam = a; a = b; b = tam; } VC & BB 35 3. Hàm gọi đệ qui: một lệnh trong thân hàm gọi đến chính nó. Số lần gọi này phải có giới hạn (điểm dừng)  Ví dụ: chương trình tính giai thừa của n. #include int giaiThua(int n); void main() { int gt4, gt7; gt4 = giaiThua(4); gt7 = giaiThua(7); cout << “4! =“ << gt4 << endl; cout << “7! =“ << gt7 << endl; } int giaiThua(int n) { int gt; if(n==1) return(1); // goi de qui gt = giaiThua(n-1)*n; return gt; } Đệ quy VC & BB 36 Đặc điểm của hàm đệ qui:  Chương trình viết rất gọn,  Việc thực hiện gọi đi gọi lại hàm rất nhiều lần phụ thuộc vào độ lớn của đầu vào. Do đó chương trình sẽ mất thời gian để lưu giữ các thông tin của hàm gọi trước khi chuyển điều khiển đến thực hiện hàm được gọi. Mặt khác các thông tin này được lưu trữ nhiều lần trong ngăn xếp sẽ dẫn đến tràn ngăn xếp nếu n lớn.  Tuy nhiên, đệ qui là cách viết rất gọn, dễ viết và đọc chương trình, mặt khác có nhiều bài toán hầu như tìm một thuật toán lặp cho nó là rất khó trong khi viết theo thuật toán đệ qui thì lại rất dễ dàng. Đệ quy VC & BB 37 Lớp các bài toán giải được bằng đệ qui  Giải quyết được dễ dàng trong các trường hợp riêng gọi là trường hợp suy biến hay cơ sở, trong trường hợp này hàm được tính bình thường mà không cần gọi lại chính nó,  Đối với trường hợp tổng quát, bài toán có thể giải được bằng bài toán cùng dạng nhưng với tham đối khác có kích thước nhỏ hơn tham đối ban đầu. Và sau một số bước hữu hạn biến đổi cùng dạng, bài toán đưa được về trường hợp suy biến. Đệ quy VC & BB 38  Các ví dụ  Ví dụ 1 : Tìm UCLN của 2 số a, b. Bài toán có thể được định nghĩa dưới dạng đệ qui như sau: − nếu a = b thì UCLN = a − nếu a > b thì UCLN(a, b) = UCLN(a-b, b) − nếu a < b thì UCLN(a, b) = UCLN(a, b-a)  Chương trình đệ qui để tính UCLN của a và b như sau. int UCLN(int a, int b) // qui uoc a, b > 0 { if (a < b) UCLN(a, b-a); if (a == b) return a; if (a > b) UCLN(a-b, b); } Đệ quy VC & BB 39  Ví dụ 2 : Tính số hạng thứ n của dãy Fibonaci là dãy f(n) được định nghĩa: − f(0) = f(1) = 1 − f(n) = f(n-1) + f(n-2) với n ≥ 2. long Fib(int n) { long kq; if (n==0 || n==1) kq = 1; else kq = Fib(n-1) + Fib(n-2); return kq; } Đệ quy VC & BB 40 Nạp chồng hàm (Function overloading)  Nạp chồng hàm là dùng chung một danh hiệu để đặt tên cho các hàm khác nhau.  Chỉ nạp chồng hàm đối với những hàm giống nhau về bản chất, nhưng khác nhau ở số lượng, và kiểu dữ liệu của các tham số.  Khả năng nạp chồng hàm kết hợp với hàm có tham số với giá trị ngầm định có thể gây ra tình trạng nhập nhằng, mơ hồ void F(int, double) { . } void F(int) { . } void F(double) { } void main() { double x = 20.0; int y = 10; F(x, y); // mơ hồ! chương trình dịch không biết gọi hàm nào } VC & BB 41 Một số gợi ý khi thiết kế hàm  Xác định rõ chức năng, nhiệm vụ của hàm.  Chỉ nên thiết kế hàm theo phương châm “mỗi hàm chỉ thực hiện một nhiệm vụ duy nhất”, và nên thiết kế sao cho có thể sử dụng lại hàm để hổ trợ cho các việc khác (reusable).  Đặt tên hàm sao cho có tính gợi nhớ (Memonic)  Nên đặt chú thích, ghi rõ các thông tin về hàm như chức năng, điều kiện dữ liệu vào, xác định dữ liệu ra của hàm, . . .  Xác định trị trả về: hàm có cần trả về giá trị? Nếu có, xác định rõ kiểu trả về. Đối với các hàm có chức năng nhập/xuất dữ liệu, trị trả về thường là void. Còn đối với loại hàm kiểm tra một tính chất P nào đó, ta thường trả về giá trị 0 hoặc 1, i.e. trả về trị của một biểu thức logic. VC & BB 42 Một số gợi ý khi thiết kế hàm  Xác định số lượng tham số và kiểu của từng tham số: hàm có nhận tham số hay không? Bao nhiêu tham số? Kiểu của từng tham số?  Xác định rõ phương pháp truyền tham số: nếu không có nhu cầu làm thay đổi trị của tham số thực truyền vào cho hàm thì áp dụng phương pháp truyền bằng giá trị. Còn ngược lại thì áp dụng cách truyền bằng tham chiếu. VC & BB 43 Phạm vi (scope) của các đối tượng  Phạm vi là vùng chương trình mà đối tượng được nhận biết và có thể được sử dụng.  Phạm vi của một đối tượng trải dài từ nơi nó được khai báo đến cuối khối, hàm, hay tập tin chứa đối tượng đó. Có các loại phạm vi sau:  Phạm vi cục bộ (local scope) − Phạm vi khối (Block scope) − Phạm vi hàm (Function scope)  Phạm vi toàn cục (global scope) − Phạm vi tập tin (File scope) − Phạm vi chương trình (Program scope) VC & BB 44  Phạm vi khối: Trong C, một khối được giới hạn bởi ngoặc {}. Biến khai báo trong khối đó có phạm vi khối, nghĩa là nó chỉ hoạt động trong khối đó mà thôi. Phạm vi này còn gọi là cục bộ, và biến đưọc gọi là biến cục bộ.  Ví dụ: int main() { int i; /* block scope */ . . . return 0; } Phạm vi (scope) của các đối tượng VC & BB 45  Ví dụ: 1: /* Scopes in nested block */ 2: #include 3: 4: main() 5: { 6: int i = 32; /* block scope 1*/ 7: 8: cout<<"Within the outer block: i=“<<i<<“\n”; 9: 10: { /* the beginning of the inner block */ 11: int i, j; /* block scope 2, int i hides the outer int i*/ 12: 13: cout<<"Within the inner block:\n"; 14: for (i=0, j=10; i<=10; i++, j--) 15: cout<<"i=“<<i<<“ j= “<<j<<“\n”; 16: } /* the end of the inner block */ 17: cout<<"Within the outer block: i=<< i<<endl; 18: return 0; 19: } Kết quả: Within the outer block: i=32 Within the inner block: i= 0, j=10 i= 1, j= 9 i= 2, j= 8 i= 3, j= 7 i= 4, j= 6 i= 5, j= 5 i= 6, j= 4 i= 7, j= 3 i= 8, j= 2 i= 9, j= 1 i=10, j= 0 Within the outer block: i=32 Phạm vi (scope) của các đối tượng VC & BB 46  Phạm vi hàm: chỉ định một biến có phạm vi hoạt động từ đầu đến cuối một hàm (không nhầm lẫn với biến có phạm vi khối). Trong C, chỉ có nhãn (label) đối với lệnh goto là có phạm vi hàm.  Ví dụ : int main() { int i; /* block scope */ . . start: /* A goto label has function scope */ . . goto start; /* the goto statement */ . . return 0; } Phạm vi (scope) của các đối tượng VC & BB 47  Phạm vi chương trình: Biến có phạm vi chương trình khi nó được khai báo bên ngoài các hàm.  Ví dụ: int x = 0; /* program scope */ float y = 0.0; /* program scope */ int main() { int i; /* block scope */ . . return 0; }  Biến này còn gọi là biến toàn cục Phạm vi (scope) của các đối tượng VC & BB 48  Ví dụ: 1: /* Program scope vs block scope */ 2: #include 4: int x = 1234; /* program scope */ 5: double y = 1.234567; /* program scope */ 7: void function_1() 8: { 9: cout<<"From function_1: x=,”<<x<<“ y= “<< y<<endl; 10: } 12: main() 13: { 14: int x = 4321; /* block scope 1*/ 16: function_1(); 17: cout<<"Within the main block: x= “<<x<<“ y= “<<y; 18: /* a nested block */ 19: { 20: double y = 7.654321; /* block scope 2 */ 21: function_1(); 22: cout<<"Within the nested block: x= “<<x<<“ y=“<<y<<endl; 23: } 24: return 0; 25: } Kết quả: From function_1: x=1234, y=1.234567 Within the main block: x=4321, y=1.234567 From function_1: x=1234, y=1.234567 Within the nested block: x=4321, y=7.654321 Phạm vi (scope) của các đối tượng VC & BB 49  Phạm vi tập tin: Trong C, biến được khai báo là toàn cục và static được gọi là có phạm vi tập tin. int x = 0; /* program scope */ static int y = 0