מבוא לתכנות ולמדעי המחשב בשפת C/תרגיל 3
תרגיל 3 -פונקציות (פרוצדורות)
מימוש פונקציות פשוטות
[עריכה]כיתבו בקובץ q1.c את מימוש הפונקציות הבאות:
int mySign(double x) {
// Returns 1 if x is a positive number, -1 if it is a negative and 0 if it is zero.
...
}
double myAbs(double x) {
// Returns the absolute value of x
...
}
void count(int from, int to) {
// Prints the numbers between 'from' and 'to' .
// if to > from, it prints from, from+1, from+2, .. , to
// if to <= from, it prints from, from-1, from-2, .., to
...
}
int myRound(double x) {
// Round x to the nearest integer. If there are two with the same distance, assume
// x had some more decimal digits that are not zero
...
}
בשביל לממש את הפונקציות, תוכלו להשתמש בפונקציית ה ()floor שנמצאת ב math.h ומחשבת את הערך השלם של מספר נתון. כמובן, הרעיון שתממשו את הלוגיקה של כל פונקציה בעצמכם ולא תשתמשו בפונקציות מקבילות ב math.h כמו fabs, למשל.
פונקציית ה - main של q1.c שלכם, תהיה הפונקציה הבאה:
int main() {
printf("mySign(5.5) = %d\n",mySign(5.5));
printf("mySign(-5.5) = %d\n",mySign(-5.5));
printf("myAbs(5.5) = %lf\n",myAbs(5.5));
printf("myAbs(-5.5) = %lf\n",myAbs(-5.5));
printf("counting from 1 to 10:\n");
count(1,10);
printf("counting from 10 to 1:\n");
count(10,1);
double checks[] = {0,5,5.4,5.5,5.6};
int n = sizeof(checks)/sizeof(double),i;
for(i=0; i<n; ++i) {
double x = checks[i];
printf("myRound(%lf) = %d\n", x, myRound(x));
printf("myRound(%lf) = %d\n", -x, myRound(-x));
}
return 0;
}
הפלט של התוכנית שלכם אמור להיות זהה לפלט המתואר בקובץ q1_ori.out. בכדי לוודא שהוא אכן זהה, הפנו אותו ראשית לקובץ ייעודי:
./q1 > q1.out
ואז השוו את קובץ הפלט שלכם, אם q1_ori.out שהורדתם לתקיית התרגיל:
diff q1.out q1_ori.out
אם הקבצים אכן זהים, לא יודפס כלום. אם יש בינהם הבדלים, הם יודפסו.
ציור קו ישר
[עריכה]בשאלה זו תתבקשו לממש פונקציה המציירת קו ישר על לוח ציור הדומה לזה שבדוגמה הגאומטרית של שיעור 9. גודל לוח הציור יהיה 30 שורות על 70 עמודות והפונקציה שעליכם לממש היא:
void line(int r1, int c1, int r2, int c2) {
// Draw a straight line from (r1,c1) to (r2,c2)
// r1 - the row of the start point
// c1 - the column of the start point
// r2 - the row of the end point
// c2 - the column of the end point
...
}
לאחר שמימשתם את הפונקציות הדרושות, אפשר יהיה לכתוב main כזה:
int main() {
initBoard();
line(1,1,10,60);
line(10,60,25,55);
line(25,55,20,5);
line(20,5,1,1);
printBoard();
return 0;
}
שייתן את הפלט:
......................................................................
.@@@@.................................................................
.@...@@@@@@...........................................................
.@.........@@@@@@@....................................................
..@...............@@@@@@..............................................
..@.....................@@@@@@@.......................................
..@............................@@@@@@@................................
..@...................................@@@@@@..........................
..@.........................................@@@@@@@...................
...@...............................................@@@@@@.............
...@.....................................................@@@@.........
...@........................................................@.........
...@.......................................................@..........
....@......................................................@..........
....@......................................................@..........
....@.....................................................@...........
....@.....................................................@...........
....@.....................................................@...........
.....@...................................................@............
.....@...................................................@............
.....@@@@@...............................................@............
..........@@@@@@@@@@....................................@.............
....................@@@@@@@@@@..........................@.............
..............................@@@@@@@@@@................@.............
........................................@@@@@@@@@@.....@..............
..................................................@@@@@@..............
......................................................................
......................................................................
......................................................................
......................................................................
הקובץ q2.c שעליכם להגיש, יכלול את הפונקציות שמימשתם ואת ה - main הבא:
int main() {
initBoard();
int ry = ROWS/2, rx = COLS/2, y = ROWS/2, x = COLS/2;
double alpha;
for(alpha = 0; alpha < 2*M_PI; alpha += .5) {
int y1 = round( y+sin(alpha)*ry );
int x1 = round( x+cos(alpha)*rx );
line(y, x, y1, x1);
}
printBoard();
return 0;
}
ROWS אמור להיות משתנה גלובאלי שערכו 30 ו - COLS משתנה גלובאלי שערכו 70.
הפלט שלו אמור להיות:
............................@.........................................
............................@................@........................
.............................@..............@.........................
.............................@..............@.........................
............@@................@............@...............@@.........
..............@@..............@...........@..............@@...........
................@@.............@.........@.............@@.............
..................@@...........@.........@...........@@...............
....................@@..........@.......@.........@@@.................
......................@@........@......@........@@....................
..@@@@..................@@.......@.....@......@@......................
......@@@@@@..............@@.....@....@....@@@...................@@@@@
............@@@@@@@.........@@....@..@...@@..............@@@@@@@@.....
...................@@@@@@@....@@..@.@..@@.......@@@@@@@@@.............
..........................@@@@@@@@.@@@@.@@@@@@@@......................
...........................@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.........@@@@@@@@@@@@@@@@@@....@@@@@@@@@@@............................
@@@@@@@@@...................@@@..@.@..@...@@@@@.......................
.........................@@@....@..@...@@......@@@@...................
......................@@@......@....@....@.........@@@@...............
..................@@@@........@.....@.....@@...........@@@@@..........
...............@@@...........@......@.......@...............@@@@......
............@@@............@@.......@........@..................@@@...
.........@@@..............@.........@.........@@......................
.......@@................@..........@...........@.....................
........................@...........@............@@...................
.......................@............@..............@..................
......................@..............@..............@@................
.....................@...............@................@...............
....................@................@................................
הערה:ייתכנו הבדלים קלים בין הפלט המוצג כאן והפלט שלכם. למרות זאת, הקווים שהתוכנית שלכם אמורה לצייר, אמורים להיות אופטימאליים מבחינת היושר והדקות כמו הקווים שבפלט הזה.
ציור רחובות תוך שימוש בספרייה גרפית
[עריכה]בשאלה זו תתבקשו להשתמש באלמנט קוד מוכן שיאפשר לכם ציור של גרפיקה עדינה. ראשית עליכם להוריד אותו מהרשת ע"י הכנסת הפקודה הבאה ב - terminal:
sudo apt-get update
sudo apt-get -y install libgtk2.0-dev
הפקודה מתקינה על מחשבכם את הספריה הגראפית +Gtk. במהלך ההתקנה תתבקשו להכניס את הסיסמה שלכם (mosenzon, password או הסיסמה אליה החלפתם) ולאחר מכן יש להקיש Enter על כל שאלה במהלך ההתקנה. את הפעולה הזאת תזדקקו לבצע רק פעם אחת על מחשבכם והיא תמשך בערך 7 דקות.
השלב השני הוא להוריד את שלושת הקבצים הבאים לתקייה בה אתם פותרים את התרגיל (בתקווה שאתם מקצים תקייה יעודית לכל תרגיל): q3.c, q3_main.c ו- Makefile. אפשרות נוספת היא להוריד את שלושת הקבצים הללו כקובץ ארכיון אחד בשם q3_ori.tgz ואז לחלץ ממנו את שלושת הקבצים ע"י הפקודה:
tar xzvf q3_ori.tgz
הקובץ q3.c הוא הקובץ שבו תכתבו את הקוד שלכם. שני הקבצים האחרים אמורים להישאר ללא שינוי. שלושת הקבצים יחד מהווים יחד תוכנית שניתן להריץ. במקום פקודת הקומפילציה שאתם רגילים לכתוב (gcc -Wall..) כותבים פשוט:
make
הפקודה הזו תפעיל את הקומפיילר ותייצר לכם את קובץ ההרצה שלכם - q3. אם תריצו אותו, אמור להופיע חלון חדש עם כפתור עליו כתוב press me. כאשר תלחצו עליו, אתם אמורים לראות את השירטוט הבא:
כל זאת, לפני שהוספתם את הקוד שלכם.
כאמור, הקובץ q3.c הוא הקובץ היחיד בו אתם אמורים לכתוב את הקוד שלכם. כרגע הוא מכיל מספר דוגמאות שאתם אמורים להחליף בקוד שלכם. קיראו את הדוגמאות ואת התיעוד שלהם בעיון. כפי שתוכלו לראות, הפונקציה שתופעל בלחיצת הכפתור 'press me' היא myFunc. השירות הגרפי היחיד שמסופק לכם הוא הדפסה של נקודה בודדת באחד מארבעה צבעים.
המשימה שלכם בתרגיל היא לממש את הפונקציות:
void drawStreet(int x, int y, int size, int n) {
// Draw a street of alternate colors of trees and houses
// (x,y) - location of the top of the leftmost house
// size - of houses
// n - number of house and trees
// The size of the trees varies randomly withing a certain range.
...
}
void drawSun(int x, int y, int r, char c1, char c2) {
// Draw a "Sun"
// (x,y) - location of the sun's center
// r - radius of the sun
// c1,c2 - alternating colors
...
}
וכל פונקציית עזר נוספת שתרצו.
המימוש של myFunc בקובץ ההגשה יהיה המימוש הבא:
void myFunc() {
drawStreet(30,60,15,6);
drawStreet(55,120,30,4);
drawStreet(70,220,50,3);
drawSun(40,40,30,'r','g');
}
אחרי שתממשו את הפונקציות, לחיצה על press me אמורה לתת ציור דומה לזה:
כפי שניתן לראות, כל רחוב בנוי מבתים בגודל רצוי ועצים בגודל משתנה. צבעי העצים והבתים מתחלפים. השמש היא פשוט אוסף רדיוסים בעלי שני צבעים מתחלפים. אלה המאפיינים שצריכים להיות גם לציור שלכם, הוא לא צריך להראות בדיוק כמו זה שבתמונה.
קובץ הריצה q3 מדגים את פעולת התוכנית.
סביר להניח שאחת הפונקציות הראשונות שתירצו לממש היא פונקציית קו ישר המשתמשת בקוד מהשאלה הקודמת (עם התאמות קלות).
בונוס (15 נק')
[עריכה]הוסיפו ל q3.c פונקציה נוספת:
void filledTriangle(int x1, int y1, int x2, int y2, int x3, int y3, char color) {..}
הפונקציה מקבלת קואורדינטות של שלושה קודקדי משולש (כלשהו) ומציירת את המשולש כאשר הוא מלא (צבוע) בצבע המבוקש.
הוסיפו ל ()myFunc קוד שמדגים שימוש בפונקציה שהוספתם.
הגשה
[עריכה]מועד הגשה: יום ראשון ה - 27.11.11, עד סוף היום.
יש להגיש ב"תרגיל שלישי" במודל, קובץ ex3.tgz המכיל את q1.c, q2.c, q3.c, Makefile, q3_main.c. אל תשכחו לבדוק את ex3.tgz לפני ההגשה ולוודא שהוא מכיל את כל מה שהוא אמור להכיל. הסיבה שיש לכלול גם שני קבצים שלא אתם כתבתם היא הקלה על פעולת ההידור של הבודק.
איכות הקוד: שימו לב שכל הפונקציות שלכם מתועדות: הסבר - מה הן מבצעות? מה משמעות הפרמטרים שהן מקבלות? מה משמעות ערך ההחזרה?
הקפידו על שמות משמעותיים לפונקציות.
בבדיקת תרגיל זה ירד ניקוד על עימוד (indentation) שגוי.
התרגיל הנוכחי דורש יותר מהתרגילים הקודמים.
בהצלחה!
פתרון
[עריכה]q1.c
[עריכה]#include <stdio.h>
#include <math.h>
int mySign(double x) {
// Returns 1 if x is a positive number, -1 if it is negative and 0 if it is zero.
if(x < 0)
return -1;
else
if(x>0)
return 1;
return 0;
}
double myAbs(double x) {
// Returns the absolute value of x
return mySign(x)*x;
}
void count(int from, int to) {
// Prints the numbers between 'from' and 'to' .
// if to > from, it prints from, from+1, from+2, .. , to
// if to <= from, it prints from, from-1, from-2, .., to
int i;
int d = mySign(to-from);
for(i=from; i*d <= to *d; i+=d) {
printf("%d\n",i);
}
}
int myRound(double x) {
// Round x to the nearest integer. If there are two with the same distance, assume
// x had some more decimal digits that are not zero
double fx = floor(x);
double d = x -fx ; // d in [0,1)
if(x < 0)
if(d <= 0.5)
return fx;
else
return fx+1;
else
if(d >= 0.5)
return fx+1;
else
return fx;
}
int main() {
printf("mySign(5.5) = %d\n",mySign(5.5));
printf("mySign(-5.5) = %d\n",mySign(-5.5));
printf("myAbs(5.5) = %lf\n",myAbs(5.5));
printf("myAbs(-5.5) = %lf\n",myAbs(-5.5));
printf("counting from 1 to 10:\n");
count(1,10);
printf("counting from 10 to 1:\n");
count(10,1);
double checks[] = {0,5,5.4,5.5,5.6};
int n = sizeof(checks)/sizeof(double),i;
for(i=0; i<n; ++i) {
double x = checks[i];
printf("myRound(%lf) = %d\n", x, myRound(x));
printf("myRound(%lf) = %d\n", -x, myRound(-x));
}
return 0;
}
q2.c
[עריכה]#include <stdio.h>
#include <math.h>
int ROWS = 30, COLS = 70;
int board[30][70];
void plot(int r, int c, char symbol){
if(r>=0 && r<ROWS && c >=0 && c < COLS)
board[r][c] = symbol;
}
void initBoard() {
int i,j;
for(i=0; i<ROWS; ++i)
for(j=0; j<COLS; ++j)
board[i][j] = '.';
}
void printBoard() {
int i,j;
for(i=0; i<ROWS; ++i) {
for(j=0; j<COLS; ++j)
printf("%c",board[i][j]);
printf("\n");
}
}
int mySign(double x) {
if(x<0)
return -1;
if(x>0)
return 1;
return 0;
}
double myAbs(double x) {
if(x>=0)
return x;
return -x;
}
void line(int r1, int c1, int r2, int c2) {
// Draw a straight line from (r1,c1) to (r2,c2)
// r1 - the row of the start point
// c1 - the column of the start point
// r2 - the row of the end point
// c2 - the column of the end point
double dr = r2-r1, dc = c2-c1;
double m = dr/dc;
if(myAbs(m) <= 1) {
int x;
int d = mySign (dc);
for(x = c1; x*d <= c2*d; x += d) {
double y = r1+(x-c1)*m;
plot(round(y),x,'@');
}
} else {
int y;
int d = mySign (dr);
for(y = r1; y*d <= r2*d; y += d) {
double x = c1+(y-r1)*(1/m);
plot(y,round(x),'@');
}
}
}
int main() {
initBoard();
int ry = ROWS/2, rx = COLS/2, y = ROWS/2, x = COLS/2;
double alpha;
for(alpha = 0; alpha < 2*M_PI; alpha += .5) {
int y1 = round( y+sin(alpha)*ry );
int x1 = round( x+cos(alpha)*rx );
line(y, x, y1, x1);
}
printBoard();
return 0;
}
q3.c
[עריכה]#include <math.h>
#include <stdlib.h>
int mySign(double x) {
if(x<0)
return -1;
if(x>0)
return 1;
return 0;
}
double myAbs(double x) {
if(x>=0)
return x;
return -x;
}
void line(int x1, int y1, int x2, int y2, char c) {
// draw a stright line from (x1,y1) to (x2,y2). c is its color
double dy = y2-y1, dx = x2-x1;
double m = dy/dx;
if(myAbs(m) <= 1) {
int x;
int d = mySign (dx);
for(x = x1; x*d <= x2*d; x += d) {
double y = y1+(x-x1)*m;
point(x,round(y),c);
}
} else {
int y;
int d = mySign (dy);
for(y = y1; y*d <= y2*d; y += d) {
double x = x1+(y-y1)*(1/m);
point(round(x),y,c);
}
}
}
void recrangle(int x, int y, int width, int height, char c) {
line(x,y,x+width,y,c);
line(x,y,x,y+height,c);
line(x+width,y,x+width,y+height,c);
line(x,y+height,x+width,y+height,c);
}
void square(int x, int y, int size, char c) {
recrangle(x,y,size,size,c);
}
void triangle(int x1, int y1, int x2, int y2, int x3, int y3, char c) {
line(x1,y1,x2,y2,c);
line(x2,y2,x3,y3,c);
line(x3,y3,x1,y1,c);
}
void house(int x,int y, int size, char c) {
triangle(x,y,x-size,y+size,x+size,y+size,c);
recrangle(x-(.7)*size,y+size,(1.4)*size,(1.5)*size,c);
recrangle(x-size/3,y+1.6*size, size/3, .9*size,c);
square(x+0.3*size,y+1.2*size,size/3,c);
}
void tree(int x, int y, int size, char c) {
recrangle (x,y,2,size,c);
int i;
for(i=0; i<size/3; ++i) {
line(x,y+i*2,x-size/4,y+i*2+size/4,c);
line(x,y+i*2,x+size/4,y+i*2+size/4,c);
}
}
void drawSun(int x, int y, int r, char c1, char c2) {
// Draw a "Sun"
// (x,y) - location of the sun's center
// r - radius of the sun
// c1,c2 - alternating colors
double alpha;
char c = c1;
for(alpha=0; alpha < 2*M_PI; alpha += 0.1) {
line(x,y,x+cos(alpha)*r,y+sin(alpha)*r,c);
if (c==c1)
c = c2;
else
c = c1;
}
}
char colors[] = {'r','b','g','l'};
void drawStreet(int x, int y, int size, int n) {
// Draw a street of alternate colors of trees and houses
// (x,y) - location of the top of the leftmost house
// size - of houses
// n - number of house and trees
// The size of the trees varies randomly withing certain range.
int c = 0,i;
double g = 3;
for(i=0; i<n; ++i) {
house(x+size*g*i,y,size,colors[c]);
double rnd = (random()%20)/10.0-1;
double treeSize = (2+rnd)*size;
tree(x+size*g*i+size*1.5, y+(2.5)*size-treeSize, treeSize, colors[c]);
c = (c+1)%4;
}
}
void myFunc() {
drawStreet(30,60,15,6);
drawStreet(55,120,30,4);
drawStreet(70,220,50,3);
drawSun(40,40,30,'r','g');
}