מבוא לתכנות ולמדעי המחשב בשפת C/מתמטיקה, הערות ומשפט תנאי.

תבנית:ניווט מבוא


שימוש בכלים מתמטיים

עריכה

עד כה ראינו שימוש באריתמטיקה בסיסית - ארבעת פעולות החשבון. בכדי להשתמש בכלים מתמטיים נוספים, יש להוסיף שורה אחת שגורמת להכללת הספריה המתמטית של C. לדוגמה:


#include <stdio.h>
#include <math.h> 
 
int main() {
  
	printf("sqrt(144) = %f, fabs(-14) = %f, sin(M_PI/2) = %f, pow(2,8) = %f \n", sqrt(144), fabs(-14), sin(M_PI/2), pow(2,8) ); 
	return 0; 
}

פלט:

sqrt(144) = 12.000000, fabs(-14) = 14.000000, sin(M_PI/2) = 1.000000, pow(2,8) = 256.000000

הסבר: השורה השניה בקוד היא זו שאחראית להכללת הספריה המתמטית, האלמנט שמאפשר שימוש בשאר הכלים המתמטיים שבהמשך הקוד. החישובים לפי סדר הדפסתם הם: השורש הריבועי (square root) של 144, הערך המוחלט (absolute value) של 14-, הסינוס של חצי פאי רדיאנים והערך של 8^2 (2 בחזקת 8).

בכדי לאפשר את שילוב הספריה המתמטית יש להוסיף גם את הפרמטר lm- לפקודת ההידור:

gcc -lm check.c -o check

לא נכנס כרגע לסיבה שבגללה יש צורך בתוספת הזאת.

הספריה המתמטית מספקת עוד הרבה כלים, ניתן לראות פירוט שלהם כאן.

קוד מסכם לידע עד כה

עריכה

הגענו לשלב בו אנחנו יכולים לכתוב קוד שעושה משהו שימושי. התוכנית שנכתוב תקבל מהמשתמש שלושה מספרים שייצגו את הפרמטרים במשוואה ריבועית ותדפיס על המסך את הפתרונות שלה. החישוב יתבצע ע"י שימוש בנוסחה המוכרת לשורשי משוואה ריבועית:

 

(אפשר לקרוא עליה יותר בויקיפדיה)

הנה קוד אפשרי:


#include <stdio.h>
#include <math.h> 

int main() {

	printf("Please enter the three coefficients of a quadratic equation\n(a*x^2+b*x+c=0)\n"); 

	double a,b,c; 
 	printf ("a: "); 
	scanf("%lf",&a);

	printf ("b: "); 
	scanf("%lf",&b);

	printf ("c: "); 
	scanf("%lf",&c);

	double d = sqrt(b*b-4*a*c); 
	
	printf("x1 = %.2lf, x2 = %.2lf \n",(-b+d)/(2*a),(-b-d)/(2*a));    
	return 0; 
}

פלט:

Please enter the three coefficients of a quadratic equation
(a*x^2+b*x+c=0)
a: 3
b: 51
c: 210
x1 = -7.00, x2 = -10.00

הערות (comments)

עריכה

בכל שפת תכנות (לפחות כל אלה ששמעתי אודותיהן) קיימת אפשרות לכתוב תוכן שהקומפיילר מתעלם ממנו התעלמות מוחלטת. האפשרות הזאת יכולה להראות מוזר בהתחלה אבל יש לה חשיבות רבה. חשוב לזכור שלא רק הקומפיילר קורא את הקוד שאנחנו כותבים. גם אנחנו וגם אנשים נוספים שירצו להבין מה עשינו, להוסיף, לשנות או לתחזק את הקוד.

לאנשים חשוב להבין את המשמעות של הדברים שכתובים. חשוב להם להבין מה המטרה, מה תפקיד כל חלק בקוד וכו'. לקומפיילר זה לא חשוב, הוא רק מתרגם את הקוד. גם למחשב המבצע זה לא חשוב, הוא מבצע בלי להבין משמעויות. לאדם הצורך הזה קריטי, אם הוא לא מבין מה קורה בקוד, מה הכוונה, הוא לא יכל לשנות אותו, אלא רק להריץ אותו.

ב C יש שתי אפשרויות לכתוב הערות. האחת היא שימוש בסימון /*...*/. סימונים אלה פועלים כמו סוגריים שתוחמים קטע בקובץ ממנו הקומפיילר מתעלם. השניה היא שימוש בסימון // שמאפשר כתיבת הערה החל מהסימון הזה ועד לסיום השורה.

הנה דוגמה:

#include <stdio.h> 
/* This is a very interesting program. 
It manages to capture the delicate relationship between 
the computer and the user. 
*/ 
int main() {
	printf("hello lord\n"); // this is the key point 
	return 0; 
}

מבחינת התפקוד, תוכנית זו שקולה לתוכנית הראשונה שכתבנו (hello world) אבל היא כוללת הסברים שנועדו לבני אדם והקומפיילר מתעלם מהם.


שימוש נפוץ נוסף להערות נקרא comment out (ו - uncomment). לפעמים, במהלך הפיתוח אנו מעוניינים לנטרל חלקים מסויימים של הקוד ולראות איך התוכנה מתפקדת בלעדיהם. אנו לא מעוניינים למחוק אותם כי יכול להיות שנשתמש בהם בעתיד.

הנה דוגמה:

#include <stdio.h> 
/* This is a very interesting program. 
It manages to capture the delicate relationship between 
the computer and the user. 
*/ 
int main() {
/*
	printf("hello word\n"); // this is the key point 
*/ 
	return 0; 
}

במצב זה התוכנית לא מבצעת דבר אבל אפשר בקלות להחזיר אותה למצב הקודם.

יש לשים לב שהערה בתוך הערה עלולה לבלבל את הקומפיילר, כמו בדוגמה הבאה:

#include <stdio.h> 
/* This is a very interesting program. 
It manages to capture the delicate relationship between 
the computer and the user. 
*/ 
int main() {
/*
	/* bla bla */ printf("hello lord\n"); // this is the key point 
*/ 
	return 0; 
}

תוכנית זו לא תעבור הידור מכיוון שכאשר הקומפיילר מוצא את סימן "סגור הערה" הוא סוגר את ההערה החיצונית ולא את הפנימית (אין לו מנגנון של סוגריים מאוזנים). אפשר לראות את הבעיה הזאת גם כאן באמצעות מנגנון הצביעה האוטומטי שבו משתמש הויקי הזה לצבוע את הקוד. שימו לב שסוגר ההערה האחרונה הוא בצבע ירוק ושהקוד לפניו איננו צבוע כהערה.

בגלל תופעות כאלה, כדאי לשים לב ולהשתמש בשני סוגי ההערות או להשתמש בסימון // בתחילת כל שורה למטרות comment out (יש עורכי טקסט שתומכים בפעולה).


 

כדאי לדעת:

השימוש בסימון // מופיע רק בתקן של שפת C שיצא ב1999, ונקרא ANSI C99. לפני כן סגנון ההערה היחיד המותר היה /* */. לכן ייתכן שקומפיילרים ישנים יותר, או כאלו שמנסים להיות תואמים לתקן הותיק ANSI C89, לא יזהו את הסגנון החדש יותר ויציגו שגיאה.

פקודת תנאי - if

עריכה

כאמור, תוכנית מחשב היא סדרת פקודות שאמורה להביא למטרה רצויה. בכדי שנוכל לכתוב תוכניות מעניינות יש צורך בנקודות התפצלות, נקודות שבהם נשאלת שאלה שמפרידה למקרים כך שבכל מקרה מתרחש משהו אחר. הפקודה שמאפשרת זאת בשפת C היא if (אם). הנה דוגמה פשוטה:

#include <stdio.h>

int main() {

	printf("Please enter your age: "); 
	int age; 
	scanf("%d",&age); 
	
	if(age < 13) 
		printf("Hey, you're just a kid!\n");  
	else
		printf("OK, you are an adult\n"); 	
		
	return 0; 
}

הרצה בה המשתמש הקליד 34:

Please enter your age: 34 
OK, you are an adult

הרצה בה המשתמש הקליד 12:

Please enter your age: 12 
Hey, you're just a kid!

תחביר

עריכה

פקודת if יכולה להכתב בשתי דרכים. ללא else:

if (תנאי) 
    בלוק קוד

או עם else:

if (תנאי) 
    בלוק קוד	
else
    בלוק קוד

בלוק קוד יכל להיות פקודה בודדת או אוסף פקודות המוקפות בסוגריים מסולסלים.


 

כדאי לדעת:

מתכנתים נוטים לכתוב פקודה בודדת תחת if, ואז מאוחר יותר כאשר הם מוסיפים שורות לתוך הif הם מקבלים שגיאות, כי הם שכחו שיותר משורה אחת דורשת כבר סוגריים מסולסלים.

על מנת להמנע מהטעות הזו, מומלץ בכל מקרה להקיף את הבלוק של if בסוגריים מסולסלים. לדוגמה:

if (תנאי) {
    בלוק קוד	
}
else {
    בלוק קוד	
{


קינון (nesting)

עריכה

בכל בלוק קוד אפשר לכתוב כל קוד חוקי ב C. בפרט, ניתן לכתוב פקודות תנאי נוספות. כתיבה כזו נקראת קינון (nesting). כמו קן בתוך קן.

התוכנית הבאה משתמשת בקינון בשביל להבין איזה מספר הוגרל בתחום בין 1 ל 4. לאחר שמצאה את המספר, היא מדפיסה אותו באנגלית.

#include <stdio.h>

#include <stdlib.h> // required for random() and srand  
#include <time.h>   // required for time()  

int main() {

	srand(time(0));       // initialize the random generator using the time 
	int n = rand()%4 + 1; // generate a random integer in the range 1..4 
	
	printf("In English, %d is ",n); 
	if(n<=2) {
		if(n==1)
			printf("One"); 
		else 
			printf("Two");  
	}
	else {
		if(n==3)
			printf("Three");
		else
			printf("Four"); 
	}
	printf("\n"); 
	return 0; 
}

(בשלב זה אין צורך להבין את הפקודות המתועדות. כל תפקידן הוא לדאוג להגרלת מספר שלא חוזרת על עצמה)

פלט לדוגמה:

In English, 4 is Four

הרצה נוספת עשויה לתת:

In English, 3 is Three

פרדיקטים

עריכה

פרדיקטים הם הביטויים שיכולים להופיע בתור תנאים. הערך שלהם חייב להיות אמת או שקר. לדוגמה, הביטוי i<10 הוא פרדיקט. אם ערכו של i קטן מ 10 אז ערך הפרדיקט הוא "אמת" אחרת הוא "שקר". ניתן דוגמה למספר פרדיקטים נוספים בכדי להכיר את התחביר:

  • i==10 - ערך הפרדיקט "אמת" אם ורק אם הערך של i שווה ל 10. שימו לב להבדל בין פרדיקט זה לבין ביטוי ההשמה i=10.
  • i!=10 - ערך הפרדיקט "אמת" אם ורק אם הערך של i שונה מ 10.
  • i >= 10 - ערך הפרדיקט "אמת" אם i גדול או שווה ל 10. באופן דומה:i<=10 - קטן או שווה.

פרדיקטים הבנויים מפרדיקטים אחרים

  • הפרדיקט i>10 && i <20 מקבל את הערך "אמת" אם ורק אם i>10 וגם i<20. הסימון && מסמל ב C את השער הלוגי "וגם" (AND).
  • i<10 || i>20 - אמיתי כאשר i קטן מ 10 או גדול מ 20. || מסמל ב C את השער הלוגי "או" (OR)

אפשר כמובן לשלב את כולם באופנים שונים. לדוגמה:

if( (i > 10 && i <= 20) || (i >= 100 && i != 102)) {..}

שימוש בפקודת תנאי בשביל לשפר קוד קודם

עריכה

נתבונן שוב בקוד שכתבנו למציאת שורשים של משוואה ריבועית:

#include <stdio.h>
#include <math.h> 

int main() {

	printf("Please enter the three coefficients of a quadratic equation\n(a*x^2+b*x+c=0)\n"); 

	double a,b,c; 
 	printf ("a: "); 
	scanf("%lf",&a);

	printf ("b: "); 
	scanf("%lf",&b);

	printf ("c: "); 
	scanf("%lf",&c);

	double d = sqrt(b*b-4*a*c); 
	
	printf("x1 = %.2lf, x2 = %.2lf \n",(-b+d)/(2*a),(-b-d)/(2*a));    
	return 0; 
}

האם הקוד הזה עובד נכון עבור כל פלט אפשרי? האם התוכנית שלנו מכילה באגים?

מה יקרה אם נכניס את הערכים הבאים: a=1, b=0, c=1? קלט זה מייצג את המשוואה:  

משוואה ללא פתרונות ממשיים. אם ננסה להריץ את התוכנית שלנו על קלט כזה, נקבל את הפלט הבא:

Please enter the three coefficients of a quadratic equation
(a*x^2+b*x+c=0)
a: 1
b: 0
c: 1
x1 = -nan, x2 = -nan

nan (או nan-) הוא הערך שמקבלים ב C מנסיון לחשב שורש של מספר שלילי (קיצור של not a number). במקרה שלנו, תוצאה של נסיון חישוב שורש ריבועי של 3 שאיננו מספר ממשי.

נשנה את הקוד שלנו לבדוק את ערך הדסקמיננטיה ולפעול בהתאם. אם היא שווה ל 0, יש רק פתרון אחד ואם היא שלילית, אין פתרונות ממשיים:

#include <stdio.h>
#include <math.h> 
 
int main() {
 
	printf("Please enter the three coefficients of a quadratic equation\n(a*x^2+b*x+c=0)\n"); 
 
	double a,b,c; 
 	printf ("a: "); 
	scanf("%lf",&a);
 
	printf ("b: "); 
	scanf("%lf",&b);
 
	printf ("c: "); 
	scanf("%lf",&c);
 
	double d = b*b-4*a*c;
	
	if (d < 0)
		printf ("There are no real solutions to this equation\n");
	else 
		if (d==0)  
			printf("x = %.2lf \n",-b/(2*a));
		else {		   
			d = sqrt(d);
			printf("x1 = %.2lf, x2 = %.2lf \n",(-b+d)/(2*a),(-b-d)/(2*a));    
		}

	return 0; 
}

פלט:

Please enter the three coefficients of a quadratic equation
(a*x^2+b*x+c=0)
a: 1
b: 0
c: 1
There are no real solutions to this equation


Please enter the three coefficients of a quadratic equation
(a*x^2+b*x+c=0)
a: 1
b: 0
c: -1
x1 = 1.00, x2 = -1.00


האם כעת הקוד נקי מבאגים? נסו לחשוב מה קורה כאשר המשתמש מכניס ל a את הערך 0.


תבנית:ניווט מבוא