Trong âm nhạc, để phát ra được một nốt nhạc nào đĩ ta phải gọi hàm soưnd với tham số bằng chính tần số của nốt nhạc đĩ oí du muốn phát ra một nốt La trung ta gọi hàm sound880 và tần số c
Trang 1Chuong 7 MOT SO BAI TAP TONG HOP MUC TIEU CUA CHUONG NAY
> Cũng cố và hồn thiện các kiến thúc lí thuyết đã được học
> Người học cĩ khả năng sử dụng phối kết hop các kiến thức lí thuyết đã học
để giải quyết anột số lớp các bài tốn thường gặp từ đĩ dân dân hình thành
tư duy lập trình cho người học
7,1 BÀI TỐN MƠ PHƠNG
Vi du 7-1 Hãy viết chương trình mơ phơng đàn Piano trên máy tính Người
sử dụng cĩ thể đánh bản nhạc bất kì bằng cách bấm các phím :
A tương ứng với nốt La B tương ứng vời nốt Sĩ © tương ứng với nốt Đơ
D tương ứng với nốt Rê E lương ứng với nết Mi E tương ứng với nốt Fa và cuối cùng là G tương ứng với nốt Soi, : - : ,
Mỗi khi cân chuyển từ quãng 8 này sang quãng ổ kế tiếp ta bấm thêm phím
Alt (vi dụ từ La trung lên La thanh ta bấm Á' - 4), ngược lại muốn chuyển từ
quãng ổ này về quãng ổ kế trước ta bấm thêm phím Shift (vi dụ từ La trung về La trâm ta bấm Shift - A)
Giải: Để phát ra âm thanh trong ngơn ngữ lập trình C ta ding ham Sound(TanSo); ham nay khai báo trong đos.h Mỗi khi hàm được gọi, loa của PC sẽ phát ra một âm thanh tương ứng cĩ tần số đúng bằng tham số TanSo và nĩ chỉ dừng
kêu khi hàm aosornd() được gọi Trong âm nhạc, để phát ra được một nốt nhạc nào
đĩ ta phải gọi hàm soưnd với tham số bằng chính tần số của nốt nhạc đĩ (oí du
muốn phát ra một nốt La trung ta gọi hàm sound(880)) và tần số của các âm trong
quãng ở tiếp theo sẽ gấp hai lần tần số của các âm tương ứng trong quãng 8 trước
đĩ (ví dụ tần số của nốt La trung là 880 H2 thì tân số của nốt La trâm là 440 Hz va của nốt La thanh là 1760 Hz ) Do đĩ trong chương trình ta chỉ cần lưu trữ tần số của các nốt nhạc trong một quãng 8 là đủ Đề chuyển đến quãng 8 tiếp theo ta bấm kết hợp với phim Ai ngược lại ta bấm Shift Chuong trình mặc định là đang hoạt động với các âm trung Để thay đổi ta cĩ thể bấm -> (phím mãi tên phải) để chuyển sang làm việc với quãng ở tiếp theo, ngược lại bấm <- (phím mũi tên trái) '
Trang 2n9 KKRXKKXKEREXEESTEEEESEE.TETT.EEEe 014/ x/ int main()
case 48: TaoAm(HeSo, TANSOISI'2); break: MAILAY/
case 46: TaoAm(HeSo, TANSO[DO}'2); break: “Al-A*/ case 32: TaoAm(HeSo, TANSO[RE}"2); break: AI-A*/ case 77 if (HeSo <= 4) Nếu phím <- được bấm"/
HeSo*=2; break; / chuyển sang quang 8 tiép */
case 75: if (HeSo >= 1⁄4)" Nếu phím -> được bẩm */
HeSo /=2: break/* chuyển về quãng 8 trước đó? default: nosound();
- _„eMitch(ch1), ` i :
case 27: nosound(); return 0; /* Bấm ESC thoat*/
case 'a' TaoAmi(HeSo, TANSOJLA)): break: * Bam a‘?
case te: TaoAm(HeSo, TANSOI[MI|); break; Bấm e*/ case : TaoAm(HeSo, TANSO[FA)); break; * Bam f/
case'g: TaoAm(HeSo, TANSO[SOL)); break; * Bam g*f case P: TaoAm(HeSo, TANSOISIJ); break; * Bam b*/
case 'ơ: TaoAm(HeSo, TANSOIDO]); break; Bấm œ*/
case 'd: TaoAm(HeSo, TANSO[RE}); break; / Bấm d*/ case A: TaoAm(HeSo, TANSO|LA]/2); break; /" Shif-a*/ case 'E: TaoAm(HeSo, TANSO[MIp2); break; /* Shift-e*/ case 'F: TaoAm(HeSo, TANSO[FAJ2); break; Shif-f*/ case 'G’ TaoAm(HeSo, TANSO[SOLY2); break*Shift-g'/ case 'B: TaoAm(HeSo, TANSO|SIJ2); break; * Shif-b*/
case 'C: TaoAm(HeSo, TANSO(DOJ/2); break; /* Shift-c*/
case D: TaoAm(HeSo, TANSO[REJ/2); break; “ Shif-d*/ default: nosound();
173
Trang 3nosound(); /* Tắt âm trước đó */ +
sound(TG); /* Phát ra âm thanh theo tần số chỉ định */
1.2: BÀI TOÁN THONG KE
Ví dụ 7-2 Hãy viết chương trình xét xem một chương trình nguồn viết bằng một thứ ngôn ngữ nào đó đã sử dụng bao nhiêu từ khoá của ngôn ngữ này và số lần lặp của các từ khoá đó trong chương trình
Giải: Đề giải quyết được bài toán trên ta cần lưu trữ bộ từ khoá của ngôn ngữ trong một tệp (gọi là tệp từ khoá Keyword.txt) theo quy cách mỗi dòng một từ và tất
cả các chữ cái đều viết bằng chữ thường (giá thiết có không quá 100 từ khoá và mỗi
từ khoá không vượt quá 32 kí tạ) Giữa các từ sẽ được phân cách nhau bởi một số kí
tự nhất định (cho trong mảng KiTuPhanCach) Chương trình sẽ thực hiện đọc tệp các từ khoá vao mang KeyWord dé xit If, sau đó đọc lần lượt từng zử văn bản trong tệp nguồn, tách bỏ những kí tự phân cách ra khôi từ đó (do hàm GefWord thực hiện) rồi đem so sánh nó với các từ khoá trong mảng KeyWord Nếu là từ khoá thì tiến hành tìm kiếm trong một mảng /¿s chứa các từ đã được tìm thấy, nếu từ mới nhận
có mặt trong List thi tién hành tăng số lượng của nó lên 1, ngược lại tiến hành bổ sung từ khoá mới này vào danh sách từ khoá đã tìm thấy List voi số lượng là 1 Cuối cùng kết quả nhận được sẽ được ghỉ ra tệp có ten OutPutName theo khuôn dạng: Tên từ khoá tìm thấy _ số lần xuất hiện
char Ten[MAXTEN]; /* Tên từ khoá */
int n; /* Số lần xuất hiện */
} RecType;
int nKeyWord, nList;
char KeyWord[MAX][MAXTEN];
i74
Trang 4RecType List[MAX];
char InPutName[MAXTEN], OutPutName[MAXTEN];
void ReadKeyWord(char *KeyWordName);
char * GetWord(char * s, int * i);
ínt lsKeyWord(char *w);
int tnList(char *w);
void CreatList(char *name);
void WriteResult(char *name);
” tt c1 1^9414311<0100004011110.0100410<9109042111992 Í int main()
printf( An Hay nhap ten tep nguon”};
'' Đọc các từ khoá trong tệp có tên KeyWordName vào mảng từ khoá KeyWord */
void ReadKeyWord(char *KeyWordName)
{
FILE *F;
F=fopen(KeyWordName, “r");
if(F==NUI L) “Nếu không mở được tệp"/
printf(“inKhong mo duoc tep %s”, KeyWordName);
getch(); /“Dừng chương trình để xem thông báo */
int L, j=0;
175
Trang 5[x HS REARS AREER TERETE HY
/“Kiểm tra w có phải là từ khoá hay không, đúng cho giá trị 1, ngược lại cho giá trị 0*/ int IsKeyWord(char *w)
/* Tìm kiém w trong List */
/* Tạo mảng List chứa các từ khoá của tệp có tên name */
void CreatList(char *name)
printfnKhong mo duoc tep %s", name);
getch(; /*Dừng chương trình để xem thông báo */
Trang 6if(I>nList) / nếu w không thuộc danh sách đã có */
++nList; /* Bổ sung w vào cuối danh sách từ khoá */ strcpy(List[nList].Ten, w); /* Dua w vào danh sách */ List[nList].n=1; “đếm số lượng từ khoá trong danh sách*/
}
else /* Từ khoá đã có trong danh sách */
++List[i].n; /* Đếm số lần xuất hiện của từ khoá */
}
w=GetWord(s, 8i); /* Tách từ tiếp theo */
ƒclose(F);
/* Ghi List lên tệp văn bản có tên name theo quy cách */
void WriteResult(char *name)
printf(nKhong mo duoc tep %s", name);
getch(); Dimg chuang trình để xem thông bao */
exit(-1); /“Thoát*/
}
for(i=0; i<=nList; ++i)
fprintf(F, “%-16s%3din", Listfi] Ten, List(i].n);
if(ferror(F)) “Nếu có lỗi trong lúc đọc */
{
perror("Loi doc tep : ");
fclose(F}, exit(-1);
Trang 713 BÀI TOÁN VẼ ĐỒ THỊ HẦM SỐ
Ví dụ 7-3 Viết chương trình vé toa độ của hàm y=ffx) trên đoạn [a, bị lên một cửa số bất kì của màn hình Sử dụng chương trình đó vẽ đồ thị cho hàm y=3x'+2x”-6x”+1 trên đoạn {-100, 1001
Giải: Ta cần thực hiện các bước sau đây:
- Tìm hình chữ nhật chứa hình cân vẽ : Xmax-Xmin= b-a va Ymax-Ymin
- Co dan hệ số toa độ để đề thị nằm trọn trong cửa sổ cần vế
- Đối từ toạ độ đề các sang toa độ của màn hình
" WEG EA ISHN EER ISI IOC STIS SSI TEI ASSIA IAI AY
printf("\n Hay nhap khoang a, b=");
seanf(“%If%If", &a, &b);
printf(nHay nhap toa do man hinh cua ca so la x1, y1, X2, y2 = ”);
scanf(“%d%d%d%d”, &x1, &y1, &x2, &y2);
Trang 8olrser() ;
printf An ******* CHUONG TRINH VE DO THI HAM SO Y=F() t2 m - printf("\n\n Ap dung cho ham y=3x^5+2x^3-6x^2+† tren doan [-100, 100], prinff(“n\nAn phim bat ki de chay tiep");
getch();
void VeDoThi(int x1, int y1, int x2, int y2, double (*f)(double), double a, double bỳ
{
double x, y, kx, ky, Ymax, Ymin, dx ;
int i, XO, YO, Xm, Ym ;
setcolor(RED); line(x1, Y0, x2, Y0}, line(X0, y1, X0, y2);
Xx=a; Xm= XO+ (int) (x*kx); y=f(x); Ym= Y0-(int)(y*ky);
maveto(Xm, Ym); dx= (b-a)/(x2-x1); setcolor(WHITE);
Trang 9pm g1 x14 101 3140140100601444100.11.1000010119166184 9 double F(double x) /* 6 đây có thể gán cho bất kì hàm nào */
double Tam = 3*pow(x, 5)+ 2"pow(x, 3) - 6*x*x +1;
return (Tam);
7.4 BÀI TOÁN ĐỆ QUY
Vi du 7-4 Viết chương trình mô phỏng bài toán tháp Hà Nội trong chế độ đồ hoạ
Mô tả bài toán: có n đĩa kích thước nhỏ dân (có lỗ ở giữa) có thể xếp chẳng lên nhau xuyên qua một coc sao cho dia to được đặt ở dưới, đĩa nhỏ được đặt ở trên Ban đầu chồng đĩa được đặt ở cột A Hãy mô phỏng các bước chuyển đĩa từ cột A sang cột C theo nguyên tắc:
- Mỗi lân chỉ được chuyển đúng một đĩa
- Không bao giờ đĩa to được đặt lên trên đĩa nhỏ hơn
- Được phép sử dung mét coc trung gian
Đây là một bài toán rất khó nếu chúng ta không sử dụng giải thuật đệ quy Tuy nhiên vấn đẻ sẽ trở nên đơn giản hơn nếu ta để ý rằng:
- Nếu chỉ có một đĩa thì ta chỉ việc chuyển đĩa từ cột A sang cột C
- Nếu có hai đĩa ta sẽ thực hiện các thao tác chuyển đĩa như sau:
+ Chuyển đĩa trên cùng từ cột À sang cột trung gian
+ Chuyển đĩa dưới cùng từ cột A sang cột C
+ Chuyển đĩa nhỏ từ cột trung gian Sang cột C
- Nếu số đĩa từ 3 trở lên thì vấn đề bắt đầu trở nên phức tạp hơn nhiều Tuy nhiên ta có thể đưa trường hợp này về hai trường hợp đầu bằng cách coi n-1 đĩa ở trên cùng như là một đĩa và ta có thể quy về trường hợp hai đĩa như sau:
+ Chuyển n-l đĩa trên cùng sang cột trung gian
+ Chuyển đĩa thứ n sang cột C 7
+ Chuyển n-1 đĩa từ cột trung gian về cột C
Và như vậy bài toán có thể được viết đệ quy như dưới đây (dữ liệu được tổ chúc dưới dạng danh sách móc nối)
Trang 10#define DichPhai 50
#define DichTrai 50
TP th 1g ki2114014017114514177705.0104001402000499 T/
typedef struct dd
Ứ* Mỗi đĩa được đặc trưng bởi số hiệu đĩa (để biết đĩa to hay nhỏ), vị trí của đĩa (để
biết vị trí hiện tại của mỗi đĩa) và màu sắc của đĩa (các đĩa sẽ có màu khác nhau }"/ int shieudia;
{+ Mỗi cột được đặc trưng bởi các tham số là số hiệu cột (để biết là cột A, B hay C),
số lượng các đĩa hiện có trên cột và con trỏ trỏ đến đĩa dưới cùng (chính là con trổ đầu của danh sách)*/
int shieucoc;
int c;
DiaPoint "next;
}Coc;
int T1,72,73, YO, Yd, X.Y;
Coc A,B,C; / Khai báo ra 3 cột */
ĐiaPoin “Tam;
int ‘n, gd; gm, buoc, day;
void tao_dia(Coc A, DiaPoint *P},
void tao_dia_t(Coc A, DiaPoint *P);
void xoa_dia(Coc A, DiaPoint “P);
void khoi_tao(Coc *A, Coc *B, Coc *C);
void chuyen_dia(Coc * A, Coc *B, DiaPoint *P);
void thap_hn(Coc *A, Coc *B, Coc *C, DiaPoint*P);
Trang 13else
if ((tam.shieucoc)<(int)((A->shieucoc+B->shieucoc)/2)) j=j-1; else j=j+1;
P->vitridia=P->vitridia+j*CaoHon;
if (A->shieucoc>B->shieucoc)
tam.shieucoc=tam.shieucoc-i*BuocCoc;
if ((tam.shieucoc-i*BuocCoc)<B->shieucec) v=0;
Trang 14if (Q1!=NULL)
Q1=Q1->next,
else Q1=B->next;
thap_hn(B, A, C, Q1);
185
Trang 15PHU LUC
PHU LUC NHUNG KI THUAT VIET CHUONG TRINH LON
Ngôn ngữ lập trình C có nhiều phương tiện khác nhau để xây dựng những chương trình cỡ lớn và phức tạp Phần này sẽ nếu lên những kĩ thuật cần thiết khi viết các chương trình lớn
"Một chương trình có rất nhiều hàm, có cấu trúc phức tạp không thể viết chung trên một tệp tin để dịch và liên kết ngay được Việc tách một chương trình lớn và phức tạp thành nhiều tệp tin và có thể do nhiêu người khác nhau lập trình sẽ nảy sinh vấn đề dịch từng tệp tin và liên kết như thế nào cho hợp lí nhất Phương pháp dịch tách biệt và liên kết theo để án của Turbo C là giải pháp cho vấn đề đó
Giả sử chương trình này được viết và ghỉ vào tệp bp.c
Vấn để ở đây là làm thế nào để dịch và liên kết (bp.e với Main.c để có chương trình khả thi mong muốn?
1 Chức năng “ProjecUMake” của Turbo C
Chương trình biên dịch C có hai cách tổ hợp nhiều tệp tỉn nguồn để liên kết thành tệp tin khả thi Một là làm “bang tay”, tức là đưa các thông báo dịch trên dòng lệnh để dịch từng tệp tin rồi liên kết chúng lại với nhau Hai là dùng trình tiện ích make
Cách thứ nhất sẽ tương đối phức tạp khi soạn thảo chương trình từ nhiều tệp tin Hơn nữa, khi thay đổi nội dung một tệp tin nguồn nào đó, chỉ cần dich lai tep
Trang 16tin nay và liên kết lại còn các tệp tin không bị thay đổi thì không cần dịch lại Khi
số lượng tệp tin lớn sẽ rất khó theo dõi công việc này
Trong Turbo C, trình tiện ích zmake đã liên kết với môi trường kết hợp dưới tên gọi “Project/Make”, Make sẽ biết công việc hợp lí phải làm khi ta thông báo
cho nó một đanh sách những tệp tin nguồn cân dịch và liên kết
2 Tạo tệp tin PRI
Dé ding được tiện ích make, trước hết phải tạo tệp tin Projeci Tệp tin này có
tên là tên của tệp tìn *.exe cần tạo và phần mở rộng là prj Chính các tệp tin *.prj sé định hướng cho tiến trình make
Khi cần kết hợp thp.e voi ÄMain.c để có thể nhận được TongBP.exe ta phải soạn thảo tệp tin TongBP,prj bằng một trình soạn thảo văn bản nào đó (đàng luôn trình Edit trong TC) gồm hai dòng sau đây rồi ghỉ vào thư mục chủ của Turbo C:
Main.c
Tbp.c
Sau khi đã có tệp tin *.,prj, ta cần đưa tên của tệp này vào trong Project name trong menu Project theo cic bude sau :
- Chọn Menu Project bằng cách bấm Alt — P
- Chọn Project name (Emter hai lần)
- Chọn tên tệp TongBP.prj trong cửa sổ hiện ra (Enter)
3 Dịch và liên kết theo Make
Bấm FØ, môi trường kết hợp sẽ tìm các tệp tin Main.c, Thp.c và kiểm tra ngày giờ của chúng Nếu chưa có các tệp Main.obj,Tbp.obj và TongBp.exe tương
ứng, trình địch sẽ tạo ra chúng bằng cách dịch các tệp Main.c và Tbp.c một cách
độc lập tạo thành các tệp đối tượng Main.obj và Tbp.obj, sau đó các tệp này mới được liên kết với nhau một cách hợp lí để tạo ra chương trình khả thi cuối cùng TongBP.exe (đoạn mã tương ứng của hàm tbp() ở tệp tbp.obj sẽ được kết hợp vào TongBP.exe khi hàm này được tham chiếu đến) Ngược lại các tệp này sẽ được tìm kiếm và kiểm tra ngày giờ so với các tệp tỉn nguồn, nếu ngày giờ của tệp tin nguồn nào mới hơn thì tệp tin này sẽ được dịch lại Nếu tệp tin TongBp.exe có ngày giờ cũ hơn các tệp tin nguồn thì tiến trình địch và liên kết được thực hiện lại để tạo ra tệp EXE mới hơn
Khi tiến trình dịch và liên kết kết thúc nếu nhấn Alt-R để chạy chương trình thì ta nhận được:
Cho2songuyen 3 4
Tong binh phuong la: 25
4 Những ưu điển của dịch tách biệt
Với một chương trình cỡ lớn có nhiều hàm và hàng ngàn đồng lệnh, việc dịch tách biệt có các ưu điểm sau:
Tai mot thời điểm Lập trình viên chỉ soạn thảo tại một bộ phận nào đó của chương trình Những phần đã khởi tạo và dịch xong không cân dich lại, ngoại trừ
phân đang soạn thảo, như thế sẽ giảm thiểu thời gian dịch Ngoài ra, nhiều người có thể cùng tham gia viết một chương trình Phương pháp dịch tách biệt cho phép kế
187