Tải bản đầy đủ (.pdf) (49 trang)

Bài giảng Kỹ thuật lập trình: Chương 3 - TS. Vũ Hương Giang (Phần 3)

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 (1 MB, 49 trang )

• Với mỗi bài toán, làm thế nào để:
– Thiết kế giải thuật nhằm giải quyết bài toán đó
– Cài đặt giải thuật bằng một chương trình máy tính

- Hãy làm cho chương trình
chạy đúng trước khi tăng tính
hiệu quả của chương trình
- Hãy tăng tính hiệu quả của
chương trình và đồng thời thể
hiện tốt phong cách lập trình
của cá nhân
CuuDuongThanCong.com

/>

CHƯƠNG III.
CÁC KỸ THUẬT XÂY DỰNG
CHƯƠNG TRÌNH PHẦN MỀM

I.
II.
III.
IV.
V.

Mở đầu
Làm việc với biến
Viết mã chương trình hiệu quả
Thiết kế chương trình
Xây dựng hàm/thủ tục
CuuDuongThanCong.com



/>

V. CÁC KỸ THUẬT XÂY
DỰNG HÀM/THỦ TỤC

1.
2.
3.
4.

Một số khái niệm thường gặp
Nguyên tắc chung
Các quy tắc tăng tốc độ
Kỹ thuật chồng/ đa năng hóa các hàm/toán tử
CuuDuongThanCong.com

/>

1. Một số khái niệm thường
gặp
• Biểu thức (expression): tính toán giá
trị đích dựa trên giá trị nguồn
• Lệnh gán (assigment): lưu trữ giá trị
của biểu thức hoặc của biến nguồn vào
trong 1 biến đích

CuuDuongThanCong.com

/>


a. Hàm định nghĩa sẵn





Được định nghĩa trong các thư viện
Cần khai báo thư viện ở đầu chương trình để có thể dùng các hàm này
Ví dụ: trong thư viện cmath, hàm sqrt tính căn bậc hai của một số
the_root = sqrt(9.0);




9.0 : tham số, cũng có thể là một biến hoặc là một biểu thức
the_root : biến lưu kết quả trả về (3.0)
sqrt(9.0) : lời gọi hàm (kích hoạt việc thực hiện hàm sqrt), cũng có thể được sử
dụng như một biểu thức

bonus = sqrt(sales) / 10;
cout
<< “Cạnh của hình vuông có diện tích “
<< area
<< “ là “
<< sqrt(area);

CuuDuongThanCong.com

/>


b. Hàm do LTV định nghĩa
• Khai báo hàm:
– Chỉ ra cách thức gọi hàm
– Phải khai báo trước khi gọi hàm
– Cú pháp
Kiểu_trả_về Tên_hàm (Kiểu_1 tên_tham_số_1,..,
Kiểu_n tên_tham_số_n);
Kiểu_trả_về Tên_hàm (Kiểu_1,.., Kiểu_n);
//Chú thích: hàm dùng để làm gì

• Ví dụ: khai báo hàm cho phép tính tổng chi phí theo công
thức: tổng chi phí = số lượng hàng * giá mỗi mặt hàng +
5% thuế giá trị gia tăng
double total_cost(int number_par, double price_par);
double total_cost(int, double);

CuuDuongThanCong.com

/>

b. Hàm do LTV định nghĩa


Định nghĩa hàm:




Chỉ ra cách thức thực hiện nhiệm vụ của hàm

Định nghĩa trước hoặc sau khi gọi hàm đều được
Cú pháp:
Kiểu_trả_về Tên_hàm (Danh_sách_tham_số) {
//code
return kết_quả;
}



Ví dụ:
double total_cost(int number_par, double price_par) {
const double TAX_RATE = 0.05; //5% tax
double subtotal;
subtotal = price_par * number_par;
return (subtotal + subtotal * TAX_RATE);
}

CuuDuongThanCong.com

/>

c. Gọi hàm
• Tên_hàm(tham_số_1, …, tham_số_n)
• Giá trị được truyền vào các tham số của hàm sẽ được
sử dụng trong phần thân hàm.
– Pass by value: giá trị truyền vào là bản sao của giá trị lưu
trữ trong biến đóng vai trò tham số đầu vào
• Giá trị tham số không thay đổi khi được sử dụng trong thân
hàm
• Gọi hàm tham trị loại bỏ các thay đổi ngoài ý muốn lên các

tham số

– Pass by reference: giá trị truyền vào là địa chỉ của tham số
đầu vào
• Giá trị tham số có thể thay đổi khi được sử dụng trong thân
hàm, do truyền biến gốc chứ không phải bản sao
• Thay đổi giá trị của đối số trong hàm sẽ ảnh hưởng hoặc thay
đổi trực tiếp lên biến gốc

CuuDuongThanCong.com

/>

Ví dụ
void square_num (int *);

Khai báo hàm

int main()
{
int p = 5;
printf ("P is now %d\n", p);
Truyền địa chỉ của p vào hàm square_num
square_num (&p);
printf ("P is now %d\n", p); Hàm thay đổi giá trị của p thành 25
return 0;
}
void square_num (int *num)
{
(*num)= (*num) * (*num);

}

Định nghĩa hàm, tham số vào là
con trỏ num trỏ điến giá trị kiểu int

* biểu diễn giá trị mà con trỏ num trỏ
đến
CuuDuongThanCong.com

/>

Viết hàm để hoán đổi giá trị
hai biến?
void trao_doi(int so1, int so2) {
int temp;
temp = so1; so1 = so2; so2 = temp;
}
void sap_xep_mang(int *a, int n) {
int i, j, temp;
for (i=0; ifor (j=i+1; jif (a[i] < a[j]) trao_doi(a[i], a[j]);
}

CuuDuongThanCong.com

/>

Viết hàm để hoán đổi giá trị hai biến?
void trao_doi(int *so1, int *so2) {

int temp;
temp = *so1; *so1 = *so2; *so2 = temp;
}
void sap_xep_mang(int *a, int n) {
int i, j;
for (i=0; ifor (j=i+1; jif (a[i] < a[j])

trao_doi(&a[i], &a[j]);
}

CuuDuongThanCong.com

/>

d. Biến tham chiếu trong C++
• Cú pháp:
– kieuDL &ten_bien;

• Bí danh của biến khác
– Thay đổi biến tham chiếu (bí danh) sẽ làm
thay đổi giá trị của biến được tham chiếu

• Ví dụ:
– int count = 1;
– int &ref = count;
//ref là bí danh của count
– ++ref;
//tăng count lên 1,sử dụng bí danh ref


CuuDuongThanCong.com

/>

e. Tham số kiểu tham chiếu trong C++
Giống tham số được khai báo var trong Pascal
Thay đổi tham số kiểu tham chiếu (tham số hình thức) trong
thân hàm sẽ làm thay đổi tham số thực khi truyền.
void trao_doi(int &x, int &y) {
int temp = x;
x = y;
y = temp;
}
• Gọi hàm:
int a=5, b=8;
trao_doi(a, b);
• Với cách gọi hàm này, C++ tự gửi địa chỉ của a và b làm tham
số cho hàm trao_doi().



CuuDuongThanCong.com

/>

Ví dụ - Hàm tham chiếu
#include <iostream.h>
int x = 4;
int & myFunc() {

return x;
}
int main() {
cout<<"X="<cout<<"X="<myFunc() = 20; //nghĩa là x = 20
cout<<"X="<return 0;
}
CuuDuongThanCong.com

/>

2. Nguyên tắc chung
• Mỗi CTC đều phải được thiết kế tốt, có
khả năng cài đặt và kiểm thử độc lập:






CuuDuongThanCong.com

Giao diện được tối thiểu hóa
Phân tách phần giao diện và phần cài đặt
Bao gói dữ liệu
Quản lý tài nguyên trước sau như một
Thiết lập các hợp đồng và thông báo lỗi đến
cho người dùng


/>

2. Nguyên tắc chung
• Tuân thủ các quy tắc đặt ra.
– Chỉ nên vi phạm 1 quy tắc nếu được đền bù bằng thứ đáng để
mạo hiểm

• Phong cách lập trình cần phải nhất quán.
– Nếu bạn chấp nhận một quy tắc như cách thức đặt tên hàm hay
biến, hằng thì hãy tuân thủ nó trong toàn bộ chương trình.

• Mỗi chương trình con (CTC) phải có một nhiệm vụ rõ ràng.
– Có 2 loại CTC: functions và procedures. Functions chỉ nên tác
động tới duy nhất 1 giá trị - giá trị trả về của hàm
– Một CTC phải đủ ngắn để người đọc có thể nắm bắt được chức
năng của nó: tính toán, đánh giá hay biến đổi dữ liệu
– Tối thiểu hóa số các tham số của CT con.
• > 6 tham số cho 1 CTC là quá nhiều

CuuDuongThanCong.com

/>

2. Nguyên tắc chung
• Đơn giản hóa vấn đề - Problem Simplification:
– Để tăng hiệu quả của chương trình, hãy đơn giản hóa vấn
đề mà nó giải quyết.

• Đơn giản hóa mã nguồn – Code Simplification :

– Hầu hết các chương trình chạy nhanh là đơn giản.
– Vì vậy, hãy đơn giản hóa chương trình để nó chạy nhanh
hơn.

• Không ngừng nghi ngờ - Relentless Suspicion:
– Đặt câu hỏi về sự cần thiết của mỗi đoạn mã nguồn, mỗi
thuộc tính và mỗi dữ liệu thành viên trong cấu trúc dữ liệu.

• Liên kết sớm - Early Binding:
– Hãy thực hiện ngay công việc để tránh thực hiện nhiều lần
sau này.

CuuDuongThanCong.com

/>

a. Đặt tên
• Tên hàm / thủ tục:
– Phải ngắn gọn và có tính chất gợi nhớ
– Phải là động từ hoặc cụm động từ
– Bắt đầu bằng chữ in hoa/in thường một
cách thống nhất

• Tên tham số: bất kỳ tên hợp lệ nào
– Không nên trùng với tên của các biến trong
CT

CuuDuongThanCong.com

/>


b. Trừu tượng hóa quy trình nghiệp vụ
• “Black Box”: hộp đen tham chiếu tới thứ mà ta
biết cách sử dụng, nhưng ta không biết phương
thức hoạt động hay thao tác chi tiết của nó
• Người dùng chương trình không cần biết chương
trình được viết như thế nào, nhưng cần biết
chương trình đó dùng để làm gì
• Xây dựng hàm/thủ tục như là các hộp đen:
– LTV – người sử dụng hàm – cần biết hàm đó làm gì,
không cần biết làm thế nào
– LTV cần biết nếu đưa đúng các tham số vào hộp đen thì
hộp đen sẽ trả ra kết quả gì

CuuDuongThanCong.com

/>

c. Che giấu thông tin
• Thiết kế các hàm/thủ tục dưới dạng các hộp đen
chính là ví dụ của việc che giấu thông tin
– Hàm được sử dụng mà không cần biết nó được viết như
thế nào
– Thân hàm được giấu đi

• Điều này cho phép LTV
– Thay đổi hoặc nâng cao hiệu quả của hàm bằng cách
viết lại phần định nghĩa hàm
– Đọc phần khai báo hàm và các chú thích tương ứng để
biết cách sử dụng hàm


CuuDuongThanCong.com

/>

Các yếu tố ảnh hưởng đến tốc
độ chương trình
• Giải thuật và thiết kế:

– Đặc tính của từng giải thuật
– Lợi ích của cách thiết kế

• Cấu trúc phân cấp của chương trình:
– Tái sử dụng từng phần chương trình
– Hiệu năng của từng phần chương trình

• Các vòng lặp:

– Số lượng thao tác
– Dữ liệu liên quan

• Giao diện:

– Cách truy nhập vào các hàm/thủ tục
– Số lượng hàm/thủ tục định nghĩa trong giao
diện

• Các vấn đề gặp phải khi chương trình hoạt
động
CuuDuongThanCong.com


/>

Các biện pháp tăng tốc độ



Có thể tăng tốc độ bằng cách sử dụng thêm bộ nhớ ( mảng ).
Dùng thêm các dữ liệu có cấu trúc:
– Thời gian cho các phép toán thông dụng có thể giảm bằng cách sử
dụng thêm các cấu trúc dữ liệu với các dữ liệu bổ xung hoặc bằng
cách thay đổi các dữ liệu trong cấu trúc sao cho dễ tiếp cận hơn.



Lưu các kết quả được tính trước:
– Thời gian tính toán lại các hàm có thể giảm bớt bằng cách tính toán
hàm chỉ 1 lần và lưu kết quả, những yêu cầu sau này sẽ được xử lý
bằng cách tìm kiếm từ mảng hay danh sách kết quả thay vì tính lại
hàm.



Caching:
– Dữ liệu thường dùng cần phải dễ tiếp cận nhất, luôn hiện hữu.



Lazy Evaluation:
– Không bao giờ tính 1 phần tử cho đến khi cần để tránh những sự tính

toán không cần thiết.

CuuDuongThanCong.com

/>

3.1.Tính toán trước các giá trị
• Nếu phải tính đi tính lại 1 biểu thức, thì nên tính
trước 1 lần và lưu lại giá trị, rồi dùng giá trị ấy
sau này
int f(int i) {
if (i < 10 && i >= 0) {
return i * i - i;
}
static int[] values =
return 0;
{0, 0, 2, 3*3-3, ..., 9*9-9};
}
int f(int i) {
if (i < 10 && i >= 0) {
return values[i];
}
return 0;
}
CuuDuongThanCong.com

/>

3.2. Loại bỏ những biểu thức thông
thường

• Đừng tính cùng một biểu thức nhiều lần!
• Một số compilers có thể nhận biết và xử lý.
for (i = 1; i<=10; i++)
x += strlen(str);
Y = 15 + strlen(str);

len = strlen(str);
for (i = 1; i<=10; i++)
x += len;
Y = 15 + len;
CuuDuongThanCong.com

/>

3.3. Sử dụng các biến đổi số học!
• Trình dịch không thể tự động xử lý

if (a > sqrt(b))
x = a*a + 3*a + 2;
if (a*a > b)
x = (a+1)*(a+2);
CuuDuongThanCong.com

/>

×