• Tăng cường khả năng lập trình • Hiểu chi tiết về: – Thiết kế chương trình – Cấu trúc dữ liệu, giải thuật, ADT – Đa nhiệm – Mạng... Tham số của hàm cần được chỉ rõ 5.. • Gọi hàm – Cung
Trang 1Kỹ thuật lập trình
Nguyễn Diệu Hương
huongnd@it-hut.edu.vn
huongnd-fit@mail.hut.edu.vn
Trang 2Mục đích của môn học
• Hỗ trợ xây dựng chương trình
chính xác, hiệu quả, dễ sửa chữa, tái sử dụng code cao.
• Tăng cường khả năng lập trình
• Hiểu chi tiết về:
– Thiết kế chương trình
– Cấu trúc dữ liệu, giải thuật, ADT
– Đa nhiệm
– Mạng
Trang 3Yêu cầu môn học
• Bài tập lớn môn học: 30-40% Đánh giá dựa trên:
Trang 5Tài liệu tham khảo
Prentice-Hall.
Kruse et al., Prentice-Hall.
2/e, Brooks/Cole.
Addison-Wesley
A Weiss, 2/e, Addison Wesley.
Addison-Wesley.
Mehta et al, Chapman & Hall/CRC.
Trang 71 Các nguyên tắc lập trình
(Kruse, Chương 1)
Reading: C++ Programming Style Guide
Trang 81 1 Thiết kế chương trình
Trang 9Giới thiệu
• Mục đích:
– Giới thiệu phương pháp và công cụ
lập trình cho những chương trình thực tế
– Đi theo một tiếp cận logic, thống nhất– Nguyên tắc thiết kế chương trình
Trang 10Xác định mục tiêu của bài toán
• Quyết định mục đích chung
• Chỉ rõ những mục đích cụ thể
• Chia công việc thành các bài toán con cho đến khi có kích thước cô đọng
Trang 11Thiết kế chương trình
• Tổ chức tốt từng phần
• Mã lệnh phải viết rõ ràng, dễ hiểu.
• Lựa chọn cấu trúc dữ liệu phù hợp
• Phân tích thuật toán
Trang 121 2 Phong cách lập trình
Trang 14Một số chú ý khi đặt tên
1 Lựa chọn cẩn thận tên hàm, lớp,
hằng, biến toàn cục
2 Tên biến cục bộ đơn giản
3 Sử dụng tiền tố/hậu tố cho các tên
có cùng kiểu
4 Tránh đặt tên dễ nhầm lẫn, hoặc
thêm phần đuôi không có ý nghĩa
Trang 15Chuẩn bị tài liệu hướng dẫn
Trang 17Một số chú ý khi viết TLHD
3 Giới thiệu mỗi đoạn chương trình
(hàm, khối) với chú thích ngắn gọn vềmục đích và ý nghĩa
4 Chỉ rõ kết thúc của mỗi phần
5 Tránh chú thích quá kỹ không cần thiết
6 Giải thích những câu lệnh không rõ
ràng
7 Phần mã lệnh: cách chương trình hoạt
động, TLHD: tại sao chương trình hoạt động, thực hiện công việc gì
8 Chú ý: Khi chương trình thay đổi, cần
đảm bảo TLHD sẽ thay đổi tương ứng
Trang 18Ví dụ
int main( ) // Program to play Conway’s game of Life.
/* Pre : The user supplies an initial configuration of living
cells.
Post : The program prints a sequence of pictures showing
the changes in the configuration of living cells according to the rules for the game of Life.
Uses: The class Life and its methods initialize( ), print( ),
and update( ).
The functions instructions( ), user_says_yes( ) */
Trang 19Định dạng chương trình
• Canh lề giúp chương trình dễ hiểu
Thời gian đọc chương trình lớn hơn rất
nhiều so với viết chương trình
Trang 20Tinh chỉnh và module hóa
• Tinh chỉnh top-down:
– Bắt đầu bằng viết chương trình chính– Quyết định việc chia các công việc (cho lớp, hàm, CTDL)
– Tiếp tục với các hàm, lớp, CTDL
Trang 21Tinh chỉnh Top-down – Hướng dẫn
1 Sử dụng lớp để mô hình hóa những khái
Trang 22Tinh chỉnh Top-down – Hướng
dẫn
2 Mỗi hàm chỉ thực hiện một công việc
duy nhất
3 Mỗi hàm/lớp nên che giấu thông tin
4 Tham số của hàm cần được chỉ rõ
5 Các dữ liệu mà hàm sử dụng:
Æ Giữ cho các mỗi liên hệ càng đơn giản
càng tốt Tránh sử dụng biến toàn cục càng nhiều càng tốt
Æ Nếu sử dụng biến toàn cục, viết hướng
dẫn chi tiết
Trang 231 3 Cài đặt, kiểm thử, tinh
chỉnh
Trang 24Hàm giả
return(true ); }
Trang 25int grid[maxrow + 2][maxcol + 2];
// allows for two extra rows and columns
int neighbor_count(int row, int col);
};
Trang 26int Life :: neighbor_count(int row, int col)
/* Pre: The Life object contains a configuration, and the
coordinates row and col define a cell inside its hedge.
Post: The number of living neighbors of the specified cell is
returned */
{
int i, j;
int count = 0;
for (i = row − 1; i <= row + 1; i++)
for (j = col − 1; j <= col + 1; j++)
count += grid[i][j]; /* Increase the count if
Trang 27void Life :: update( )
/* Pre: The Life object contains a configuration.
Post: The Life object contains the next generation of configuration */
{
int row, col;
int new_grid[maxrow 2][maxcol 2];
for (row = 1; row <= maxrow; row )
for (col = 1; col <= maxcol; col )
for (row = 1; row <= maxrow; row )
for (col = 1; col <= maxcol; col )
grid[row][col] = new_grid[row][col];
}
Trang 28void Life :: initialize( )
/* Pre: None.
Post: The Life object contains a configuration specified by the user */
{
int row, col;
for (row = 0; row <= maxrow 1; row )
for (col = 0; col <= maxcol 1; col )
grid[row][col] = 0;
cout << "List the coordinates for living cells." << endl;
cout << "Terminate the list with the the special pair -1 -1" << endl;
cin >> row >> col;
while (row != -1 || col != -1) {
if (row >= 1 && row <= maxrow)
if (col >= 1 && col <= maxcol)
Trang 29void Life :: print( )
/* Pre: The Life object contains a configuration.
Post: The configuration is written for the user */
{
int row, col;
cout << " \nThe current Life configuration is:" << endl; for (row = 1; row <= maxrow; row ) {
for (col = 1; col <= maxcol; col )
Trang 30for (row = 1; row <= maxrow; row ){
for (col = 1; col <= maxrow; col )
cout << iConfig.neighbor_count(row,
col) ; cout << endl;
}
}
Trang 32Nguyên tắc kiểm thử chương trình
• Chọn dữ liệu kiểm thử: chất lượng quan trọng hơn số lượng
• 3 phương pháp kiểm thử:
– Hộp đen (Black-box)
– Hộp kính (Glass-box)
– Hộp tíc tắc (Ticking-box)
Trang 331 4 Bảo trì chương trình
Trang 34Bảo trì
• Sau khi chương trình đã đưa vào
sử dụng.
• Sửa đổi và phân tích chương trình
để đáp ứng yêu cầu mới.
• Bảo trì chiếm >50% tổng số công việc.
Trang 353 Giao diện thân thiện? Đầu vào/ra?
Nhiều chức năng thay thế? Trợ giúp rõ ràng?
4 Chương trình viết rõ ràng, logic? Cấu
trúc dữ liệu hợp lý?
5 Tài liệu hướng dẫn rõ ràng? (tên, đầu
vào, đầu ra, giải thích)
6 Thời gian chạy và bộ nhớ hiệu quả?
Trang 362 Hàm và biến
(Deitel, Chương 6)
Trang 37• Gọi hàm
– Cung cấp tên hàm và các tham số
– Hàm thực hiện các thao tác và các phéptoán
– Hàm trả lại giá trị kết quả
Trang 39Biến cục bộ và toàn cục
• Phạm vi biến là vùng (block)
chương trình mà biến được khai báo Không thể truy cập biến địa phương ngoài vùng chương trình này
• Biến toàn cục: phạm vi biến từ vị trí khai báo đến hết chương trình
Trang 40Sự khác nhau giữa biến cục
bộ và toàn cục
Trang 41Biến static
• Được khởi tạo ở lần chạy đầu tiên Phạm vi sử dụng:
– Khai báo trong hàm: phạm vi là hàm
mà nó khai báo Nhưng được giữ
nguyên khi ra khỏi hàm và có thể sửdụng lại khi hàm được thực hiện trởlại
– Khai báo ngoài hàm: phạm vi là từ khi khai báo đến cuối file chương trình
Trang 421 /* Fig 5.12: fig05_12.c
2 A scoping example */
3 #include <stdio.h>
4
5 void useLocal( void ); /* function prototype */
6 void useStaticLocal( void ); /* function prototype */
7 void useGlobal( void ); /* function prototype */
8
9 int x = 1 ; /* global variable */
10
11 /* function main begins program execution */
12 int main( void )
18 { /* start new scope */
19 int x = 7 ; /* local variable to new scope */
20
21 printf( "local x in inner scope of main is %d\n" , x );
22 } /* end new scope */
23
Global variable with file scope
Variable with block scope
Variable with block scope
Trang 4324 printf( "local x in outer scope of main is %d\n" , x );
25
26 useLocal(); /* useLocal has automatic local x */
27 useStaticLocal(); /* useStaticLocal has static local x */
28 useGlobal(); /* useGlobal uses global x */
29 useLocal(); /* useLocal reinitializes automatic local x */
30 useStaticLocal(); /* static local x retains its prior value */
31 useGlobal(); /* global x also retains its value */
39 /* useLocal reinitializes local variable x during each call */
40 void useLocal( void )
46 printf( "local x in useLocal is %d before exiting useLocal\n" , x );
47 } /* end function useLocal */
48
Variable with block scope
Trang 4449 /* useStaticLocal initializes static local variable x only the first time
50 the function is called; value of x is saved between calls to this
59 printf( "local static x is %d on exiting useStaticLocal\n" , x );
60 } /* end function useStaticLocal */
61
62 /* function useGlobal modifies global variable x during each call */
63 void useGlobal( void )
64 {
65 printf( "\nglobal x is %d on entering useGlobal\n" , x );
66 x *= 10 ;
67 printf( "global x is %d on exiting useGlobal\n" , x );
68 } /* end function useGlobal */
Static variable with block scope
Global variable
Trang 45
local x in outer scope of main is 5
local x in inner scope of main is 7
local x in outer scope of main is 5
local x in useLocal is 25 after entering useLocal
local x in useLocal is 26 before exiting useLocal
local static x is 50 on entering useStaticLocal
local static x is 51 on exiting useStaticLocal
global x is 1 on entering useGlobal
global x is 10 on exiting useGlobal
local x in useLocal is 25 after entering useLocal
local x in useLocal is 26 before exiting useLocal
local static x is 51 on entering useStaticLocal
local static x is 52 on exiting useStaticLocal
global x is 10 on entering useGlobal
global x is 100 on exiting useGlobal
local x in main is 5
Trang 462 3 Kiểu tham chiếu
Trang 47Kiểu tham chiếu (reference)
• Tham chiếu là một tên khác của biến
• Được xem như một con trỏ hằng
– không được tham chiếu tới một biến khác
int ival = 1024;
int &refVal = ival; // ok
int &refVal; // error: tham chiếu phải được khởi tạo
// tới một biến nào đó
refVal += 2;
refVal = someVal;
int *p = &refVal; // khởi tạo p là địa chỉ của refVal
Trang 48int **e = &d;
Dữ liệu của mảng a[ ] sau khi thực hiện những câu lệnh sau?
Trang 492 4 Truyền tham số
Trang 50Truyền tham số
• Có 2 cách truyền tham số:
– Truyền theo giá trị (call/pass by value)– Truyền theo tham chiếu (call/pass by reference)
Trang 51Truyền theo giá trị và truyền
theo tham chiếu
Truyền theo giá trị:
• Một bản copy của biến được truyền vào tham số của hàm
Truyền theo tham chiếu:
• làm việc với tham số gốc của lời gọi hàm
Trang 52void SwapWrong( short , short );
Trang 53Truyền theo tham chiếu
void SwapRef( short &, short & );
Trang 54Tham biến là kiểu con trỏ
• Cung cấp địa chỉ của biến
• Cho phép thay đổi giá trị của tham số
Trang 55Ví dụ
The code
void doubleIt( int x,
int * p ) {
p (8200)
x (8196)
16 a
a = 18
Trang 56Tham biến là con trỏ
void SwapPtr( short *, short * );
Trang 57Tham số mặc định được chỉ ra trong hàm mẫu
Trang 59Tham số mặc định
Trang 60Ví dụ
void modifyArray( int [], int );
int main ()
{
const int arraySize = 5; // size of array a
int a[ arraySize ] = { 0, 1, 2, 3, 4 }; // initialize array a
// pass array a to modifyArray by reference
modifyArray( a, arraySize );
// in function modifyArray, "b" points to the original array "a"
void modifyArray( int b[], int sizeOfArray )
{
// multiply each array element by 2
for ( int k = 0; k < sizeOfArray; k++ ) b[ k ] *= 2;
Trang 61// In function tryToModifyArray, "b" cannot be used // to modify the original array "a" in main.
void tryToModifyArray( const int b[] )
Trang 62Hàm trả lại giá trị con trỏ
Trang 63Chồng hàm – Ví dụ
• Các hàm được chồng:
void getDimensions( int , int ); // 2 void getDimensions( int , double ); // 3 void getDimensions( double , double ); // 4
• CTD sẽ gọi hàm như sau:
int length, width;
double base, height;
getDimensions(length, height); // 3
Trang 663 Đệ quy
(Kruse, Chương 5, Sedgewick, Chương 5)
Trang 673.1 Tổng quan về đệ quy
Trang 68Kỹ thuật Đệ quy
• Là một kỹ thuật giải quyết bài toán quan
trọng trong đó:
– Phân tích đối tượng thành các thành phần
nhỏ hơn mang tính chất của chính đối tượng đó.
• Ứng dụng:
– Cú pháp của ngôn ngữ lập trình
– Các thuật toán tìm kiếm và sắp xếp
– Lý thuyết trò chơi (Game theory)
– …
Trang 69Gọi hàm và Bộ nhớ Stack
Biến địa phương Địa chỉ trở về Các tham số
Activation
Record
Activation Frame
Trang 70Khung stack cho các lời gọi hàm
M M
A
M A B
M
A
M A C
M A C D
M A C
M A
Stack được cấp phát cho dữ liệu
Thời gian
M M
D
M D D
M D D D
M D D
M D
M
Trang 71Cây lời gọi hàm
Trang 72Bài toán Tính giai thừa
• Hàm tính giai thừa cho một số
Trang 73Chương trình tính giai thừa
Trang 74Điều kiện của đệ quy
• Giải thuật đệ quy phải thỏa mãn 2
điều kiện:
– Phải có điểm dừng
– Phải làm cho kích thước bài toán thu
nhỏ hơn
Trang 76Thiết kế giải thuật đệ quy
1 Tìm cách phân rã bài toán
2 Tìm điều kiện dừng
3 Viết sơ bộ thuật toán
4 Kiểm tra sự kết thúc
5 Vẽ cây đệ quy
Trang 77Cài đặt đệ quy
• Một processor.
• Đồng bộ trên nhiều processor:
Trang 78Chia để trị
• Giải quyết một bài toán:
– chia công việc thành các phần nhỏ hơn,
– mỗi phần phải dễ giải quyết hơn bài toán ban đầu
Trang 79Thông số hóa bài toán
void move(int n, int start, int finish, int temp);
/* Pre: There are at least n disks on the tower
start The top disk (if any) on each of
towers temp and finish is larger than any
of the top n disks on tower start.
Post: The top n disks on start have been
moved to finish; temp (used for
temporary storage) has been returned to its starting position
*/
Trang 80move(n-1, start, temp, finish);
cout << “ Move disk “ << n << “ from ”<<start
<< “ to ”<<finish <<endl;
hanoi(n-1, temp, finish, start);
}
}
Trang 81Các nhà sư phải chuyển 64 đĩa Giả sử mỗi lần chuyển mất 1 giây, các nhà sư sẽ phải mất 5 * 10 11 năm = 25 lần tuổi của vũ trụ Khi chuyển xong chồng đĩa thì đã đến ngày tận thế!
11
Trang 82Dãy số Fibonacci
• Dãy số Fibonacci:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34,
trong đó mỗi số là tổng của 2 số đứng trước nó.
• Định nghĩa theo đệ quy:
Trang 85Tìm kiếm nhị phân
// Tìm kiếm trên một mảng có thứ tự
int bsearchr( const int data[], // input: mảng
int first, // input: chỉ số đầu
int last, // input: chỉ số cuối
int value) // input: giá trị cần tìm // output: chỉ số của phẩn tử tìm được, nếu không trả lại –1
{ int middle = (first + last) / 2;
if (data[middle] == value)
else
}
Trang 863.4 Đệ quy đuôi
Trang 87Đệ quy đuôi
• Hành động cuối cùng của hàm là lời gọi đệ quy tới chính nó
Trang 88Đệ quy đuôi
Trang 89Xóa đệ quy đuôi
void move( int n, int start, int finish, int temp)
{
int swap; // temporary storage to swap towers
while (n > 0) { // Replace the if statement with a loop.
move(n − 1, start, temp, finish); // first recursive call
cout << " Move disk " << n << " from " << start
<< " to " << finish << endl;
n−−; // Change parameters to mimic the
// second recursive call.
Trang 903.5 Khử đệ quy
Trang 92for (int i=2; i<= n; i++)
f[i] = f[i-1] + f[i-2];
}
Trang 93Đệ quy và Vòng lặp
• Hàm đệ quy chỉ có một lời gọi đệ quy
tới chính nó:
• Nhiều lời gọi đệ quy chồng nhau:
• Mọi giải thuật đệ quy đều có thể thay
thế bằng một giải thuật không đệ quy
• Khử đệ quy:
Trang 943.6 Kỹ thuật quy hoạch
động
Trang 95Quy hoạch động – Dynamic Programming
• Là một kỹ thuật để tránh phải tính toán lại cho các giải thuật đệ quy.
• Giải pháp:
Trang 96Quy hoạch động từ dưới lên
{
f[0] = 0; f[1] = 1;
f[i] = f[i-1] + f[i-2];
}
Trang 97Quy hoạch động từ trên xuống
// Note: knownF is a global array initialized all zero
int Fib ( int i)
{
if (knownF[i]!=0) return knownF[i]; // First action
if (i < 0) return i; // parameter error
else if (i==0) return knownF[0] = 0;
else if (i==1) return knownF[1] = 1;
else return known[i] = Fib(i-1) + Fib(i-2); // last action
}
Trang 98Ví dụ
• Tính các hệ số nhị thức:
– Số các tập con gồm k phần tử từ tập gồm N phần tử