Java/תנאים ולולאות
תנאים ולולאות הם כלים בסיסיים בכל שפות התכנות העיליות, וגם בג'אווה.
תנאים
עריכהעד כה למדנו כיצד לקבל קלט מהמשתמש, וכיצד להחזיר פלט. עם זאת, היינו מוגבלים מאוד ביכולתנו: התוכניות שכתבנו ידעו לפעול רק בצורה זהה בכל פעם שהרצנו אותן, עבור כל קלט שהכנסנו. כדי שנוכל לווסת את מהלך התוכנית נשתמש במערכת של תנאים. תנאים מאפשרים לנו להגדיר לתוכנית להפעיל או לא להפעיל פקודות מסויימות בהתאם לתנאים אותם נקבע. נוכל לגרום לתוכנית "לקבל החלטות" בזמן ריצה, בהתאם למצבים אשר יכולים להשתנות מפעם לפעם. הכתיבה מתבצעת בצורה הבאה:
if (condition)
Command;
כאשר במקום המילה condition נכתוב את התנאי שלנו. התנאי הוא תמיד בוליאני, כלומר - מסוגל להיות אמת (true), או שקר (false). כאשר התשובה חיובית (התנאי מתקיים) תופעל הפקודה שכתובה מיד אחרי התנאי, וכשהתשובה שלילית התוכנית תתעלם מהפקודה שאחרי פקודת ה-if, ותמשיך הלאה. לדוגמה:
if (x==5)
System.out.println("X is 5");
תביא להדפסת המחרוזת "X is 5" אך ורק אם המשתנה x שווה ל-5. כל ערך אחר יגרום לתוכנית להמשיך הלאה. התנאי לא מוגבל לפקודה אחת: ניתן להשתמש בצומדיים ('{ }') כדי להגדיר קבוצת פקודות שתתבצענה אם יתקיים התנאי, בצורה הבאה:
if (condition) {
Command 1;
Command 2;
...
}
לפעמים נרצה להוסיף לתוכנית הוראות לביצוע גם למקרה והתנאי יהיה שקרי (לא יתקיים). נעשה זאת באמצעות הוספת בלוק else (אחרת) בצורה הבאה. בהמשך יפורט הנושא מעט יותר:
if (x == 5)
System.out.println("X is 5");
else
System.out.println("X is not 5");
בדוגמה זו אם X יהיה שווה ל-5 ההודעה "X is 5" תודפס, ואם X לא יהיה שווה ל-5 תודפס ההודעה "X is not 5".
תנאים מורכבים
עריכהתנאי יכול להיות מורכב, כלומר - מורכב מכמה תנאים בו זמנית. למשל - אנו רוצים שהתנאי יתקיים אך ורק אם משתנה מסויים יהיה גדול מ-1 אבל גם קטן מ-9. בשביל ליצור תנאים כאלה, נצטרך להכיר את משפחת האופרטורים הלוגיים. נניח שתנאי א' ותנאי ב' הם שני תנאים שונים, ואז:
- האופרטור "וגם" יהיה נכון אם ורק אם גם תנאי א' מתקיים וגם תנאי ב' מתקיים. בג'אווה אנו מסמנים אופרטור זה ב-"&&".
- האופרטור "או" יהיה נכון אם ורק אם אחד מהתנאים יתקיים. בג'אווה אנו מסמנים אופרטור זה ב-"||".
- סימן השלילה הופך את התנאי. בג'אווה אנו מסמנים זאת ב-"!".
הערה: ישנו סדר פעולות בסימנים הנ"ל בג'אווה קודם כל ! אח"כ && ואז ||. לדוגמה: התנאי((if((c1>64 && c1<92) || (c1>97 && c1<123 לא יכל להתקיים ללא הסוגריים הפנימיים שמפרידים בין הבדיקות.
דוגמאות
עריכהנניח ש-x הוא משתנה אותו הגדרנו להיות משתנה מסוג int.
if (x>4 && x<9)
הוא תנאי שיתקיים רק אם ערך המשתנה x נמצא בין 5 ל-8 (שימו לב: אם x שווה ל-4 או ל-9, התנאי לא מתקיים!)
if (x>4 || x<9)
</source> הוא תנאי שיתקיים עבור כל x שערכו קטן מ-4 או גדול מ-9.
if (!(x>4))
הוא תנאי שיתקיים עבור כל x שאינו גדול מ-4, כלומר - עבור כל x שקטן או שווה ל-4.
ניתן לחבר גם תנאים מורכבים יותר, כמו התנאי הבא:
<source lang="java">
if ((x<4 || x>9) && (y>15 && y<35))
כאשר גם y הוא משתנה מטיפוס int. תנאי כזה יתקיים רק אם יתקיים התנאי השמאלי, כלומר - x גדול מ-9 או קטן מ-4, וגם התנאי הימני, כלומר - y גדול מ-15 וקטן מ-35. בצורה זהה, ניתן לחבר מספר רב של תנאים בהתאם לצורך. בהמשך נראה דוגמאות שונות לכך - תנאים פשוטים ומורכבים.
תנאים מקוננים
עריכהראינו כבר שבתוך בלוק תנאי ניתן לכתוב פקודות רבות. הבה נראה דוגמה, בהנחה ש-str הוא משתנה מוגדר מטיפוס String:
if (str.equals("Hello"))
System.out.println("This is hello");
בדוגמה זו אנו בודקים אם המשתנה str שווה למחרוזת "Hello". מכיוון שאנו מתעסקים עם מחרוזות נעשה שימוש בפקודה equals במקום בצורת ההשוואה המוכרת (באמצעות סימני ה"=="). אם אכן str שווה למחרוזת זו, מודפס הפלט "This is hello".
באותה צורה בה כתבנו פקודות שיתבצעו אם יתקיים התנאי, ניתן גם לכתוב תנאים נוספים - תנאי בתוך תנאי. תנאים כאלה מכונים תנאים מקוננים, והם שימושיים מאוד בתכנות. נראה דוגמה דומה, והפעם עם תנאי מקונן (הפעם, str ו-str2, שניהם משתנים מוגדרים מטיפוס String):
if (str.equals("Hello"))
if (str2.equals("World"))
System.out.println("This is hello and world");
בדוגמה זו מתבצעות שתי בדיקות, בזו אחר זו: אם הבדיקה הראשונה מתקיימת, כלומר ערך המחרוזת str הוא "Hello", ממשיכה התוכנית לרוץ ולבדוק גם את התנאי השני, כלומר - בדיקה האם str2 שווה ל-"World". אם התנאי הראשון לא מתקיים - התנאי השני כלל לא נבדק.
ומה אם לא?
עריכהנניח שאנו מתכננים לבצע קטע קוד מסויים אם תנאי מתקיים, וקטע קוד שונה - אם התנאי לא מתקיים. נראה דוגמה לכך:
if (x>30 && y<10)
System.out.println("It's OK");
if (x<=30 || y>=10)
System.out.println("You're in trouble");
זוהי דוגמה פשוטה למדי - התנאי שלנו הוא שמשתנה מסוג int בשם x גדול מ-30 וגם משתנה מסוג int בשם y קטן מ-10 (כמובן, בהנחה ששני משתנים אלה כבר הוגדרו קודם). אם מתקיים אחד התנאים ההפוכים - x קטן או שווה ל-30 או y גדול או שווה ל-10 - ברור שהתנאי לא מתקיים. בדומה למרבית שפות התכנות, גם ג'אווה מציעה מנגנון שמקצר תהליך זה והופך אותו לפשוט יותר - מנגנון ה-else. קטע הקוד הבא שקול בדיוק לקטע שבתחילת הסעיף:
if (x>30 && y<10)
System.out.println("It's OK");
else
System.out.println("You're in trouble");
אם כך, בסופו של כל תנאי ניתן להוסיף else. אם התנאי לא מתקיים - יתקיים מה שכתוב בבלוק ה-else. ניתן להוסיף תנאים גם ל-else, אם כותבים else if (condition)
במקום else בלבד. לתנאי else-if כזה ניתן להוסיף else משלו, או else-if משלו, וכך ניתן ליצור שרשרת של else-if. נציג כעת דוגמה לתוכנית שמשתמשת בשרשרת else-if ובתנאים מורכבים. בניגוד למנהגנו, נציג כאן תוכנית מלאה:
import java.util.Scanner;
public class GradeSystem {
public static void main(String[] args) {
Scanner s;
s=new Scanner(System.in);
int grade=s.nextInt();
String str;
if (grade>100 || grade<0)
str="Illegal grade";
else if (grade>=90)
str="Very good";
else if (grade>=80)
str="Good";
else if (grade>=70)
str="Moderate";
else if (grade>=60)
str="Passed";
else
str="Failed";
System.out.println(str);
}
}
עכשיו תורכם: מה עושה התוכנית? מה מטרתה? |
הערה: לכל תנאי ניתן להוסיף כמה else-if שרוצים. עם זאת, ניתן לכתוב else אחד בלבד (חשבו - מדוע?). else הוא אפשרות בלבד - אין חובה להשתמש בו, אך במקרים רבים הוא נוח מאוד.
לולאות
עריכהלולאות מאפשרות לנו לחזור על ביצוע פעולה זהה מספר רב של פעמים. לפני שנמשיך, נראה דוגמה:
for (int i=0; i<50; i++)
System.out.print("*");
השורה השנייה מוכרת לנו היטב - היא מדפיסה כוכבית בודדת. השורה הראשונה מעט מורכבת יותר: נתחיל מהמילה for. זוהי מילה שמורה של ג'אווה, המסמנת לולאה. לולאה כזו נכנה בשם "לולאת for" (זאת, על מנת להבדילה מהסוג הנוסף שנכיר בקרוב). צורת הכתיבה היא כזו:
for (init; condition; step)
כאשר במקום init נכתב המצב ההתחלתי (כלומר, פקודה שתתבצע בשעה שהתוכנית נכנסת ללולאה לראשונה), במקום condition נכתוב תנאי סיום - הלולאה תמשיך לרוץ כל עוד התנאי מתקיים, ובמקום step נכתוב מה קורה בכל פעם שהתוכנית חוזרת ללולאה. בדוגמה שלנו השתמשנו בכמה תנאים נפוצים ופשוטים מאוד: בתחילת הלולאה אתחלנו משתנה מסוג int בשם i להיות 0, ופקדנו על התוכנית להפעיל את הפקודות שבתוך הלולאה (דהיינו, הפקודה להדפיס "*") כל זמן ש-i קטן מ-50. בכל פעם שמסיימת התוכנית לבצע את הפקודות, i גדל ב-1. אם כך, לאחר 50 פעמים בהן רצה הלולאה, המשתנה i יהיה שווה ל-50 - התנאי לא יתקיים, והלולאה תסתיים.
חשוב לציין כי התנאים האלו אינם הכרחיים. הפקודה for (;;)
היא חוקית - לולאה זו תרוץ לנצח. ניתן להשמיט כל אחד מהחלקים, לפי הצורך.
כעת נראה דוגמה שממחישה יתרון גדול של לולאת ה-for - השימוש במשתנה הלולאה.
for (int i=0; i<10; i++) {
System.out.println(i);
}
שורות קוד אלו יגרמו להדפסת המספרים מ-0 ועד 9 (חשבו: למה לא 1 עד 10?). נראה דוגמה מורכבת יותר:
for (int i=1; i<=10; i++) {
for (int j=1;j<=10;j++) {
System.out.print(i*j+"\t");
}
System.out.println();
}
בדוגמה זו השתמשנו בלולאות מקוננות על מנת להדפיס את לוח הכפל. תזכורת: הסימן "\t" משמש ליצירת רווח קבוע (טאב), על מנת שצורת ההדפסה תהייה נעימה יותר לעין. נעבור על אופן הריצה בצורה מפורטת:
- בשלב הראשון, נכנס המחשב ללולאה הראשונה -
for (int i=1; i<=10; i++)
. כפי שכבר ראינו, מתבצע כעת איתחול: המשתנה i מוכרז להיות משתנה מטיפוס int, ומקבל את הערך 1. - כעת, ממשיכה התוכנית ומגיעה אל תחילת הלולאה השנייה -
for (int j=1;j<=10;j++)
. גם כאן מתבצע האיתחול. כעת קיימים שני משתנים: i ו-j, שניהם משתנים מטיפוס int, ובשלב זה - שניהם שווים 1. - התוכנית מתקדמת אל השורה הבאה -
System.out.print(i*j+"\t");
, ומדפיסה את התוצאה הראשונה: 1 (שני המשתנים שווים 1 - לכן גם מכפלתם). - כאן מגיעה התוכנית אל הצומדיים שמסמנים את סוף הקטע אליו מתייחסת הלולאה השנייה (הלולאה של j). התוכנית חוזרת אל הלולאה: j גדל ב-1, והתוכנית בודקת אם הוא עדיין עומד בתנאי. j עומד בתנאי, כיוון ש-2 קטן מ-10, לכן מתבצע תוכן הלולאה פעם נוספת, הפעם - j שווה ל-2 ו-i שווה ל-1. המספר שיודפס הפעם הוא 2 (כי 2*1=2).
- הלולאה ממשיכה לחזור על עצמה עד שהתנאי מפסיק להתקיים: בפעם העשירית, j הופך להיות 11, לא עומד בתנאי, והלולאה מסתיימת. בשלב זה מודפסת כבר שורת המספרים הראשונה: 1 2 3 4 5 ... 10
- הגענו אל השורה הבאה:
System.out.println();
. גם שורה זו מיועדת למטרות נוי בלבד - תפקידה לעבור שורה בכל פעם, כדי שכל עשרה מספרים יודפסו בשורה אחת. בלי שורה זו, היו מודפסים כל המספרים בשורה אחת ארוכה. - התוכנית הגיעה אל קצה הלולאה הראשונה (לולאת ה-i). כמו עם ה-j, גם i מתקדם ב-1, עומד בתנאי (כי 2 קטן מ-10), והלולאה מתחילה מחדש, רק שהפעם - i שווה ל-2. פרט לכך, כל השלבים זהים.
- לסיכום, בכל מעבר של הלולאה החיצונית - הלולאה של i, מתבצעת ריצה מלאה של הלולאה הפנימית - הלולאה של j. כל ריצה של הלולאה הפנימית מדפיסה שורת מספרים אחת, כל מעבר של הלולאה החיצונית גורם למעבר שורה ולהמשך ההדפסה של השורה הבאה. סך הכל, מתבצעות 100 פקודות הדפסה, ו-10 פקודות מעבר שורה.
לולאות do-while
עריכהפרט ללולאת ה-for, קיים סוג נוסף של לולאה: לולאת while, או do-while. זוהי לולאה ששונה מלולאת ה-for בכך שאין בה שימוש במשתנה ש"רץ" - הלולאה מסתפקת בתנאי יציאה בלבד. לולאה כזו ניתן לכתוב בשתי צורות:
- צורה ראשונה - while בלבד:
while(condition) {
// to do
}
- צורה שנייה - do ו-while בסוף:
do {
// to do
} while(condition);
בשתי הצורות שהוצגו יבוא הקוד (הפקודות שהלולאה אמורה לבצע) במקום בו כתוב // to do
, ותנאי היציאה במקום בו כתוב condition
. ההבדל בין הצורות הוא הרגע בו נבדק התנאי: בצורה הראשונה, כאשר משתמשים ב-while בלבד, נבדק התנאי כאשר נכנסים ללולאה לראשונה, ואז - בכל פעם שמסתיימות הפקודות בבלוק הלולאה. אם משתמשים גם ב-do, נבדק התנאי בכל פעם שמסתיימת הלולאה. במקרה כזה, תמיד תתבצע הלולאה לפחות פעם אחת. כמו לולאת for, גם כאן אפשר להשתמש בלולאות מקוננות ובתנאים מורכבים.
לולאת foreach
עריכההחל מגרסת 1.5[1], ניתן להשתמש בסוגה המיוחד של לולאת for, שנקרא foreach. מבנה הלולאה הוא:
for (Class member: collection) {
//code
}
בתור דוגמה, נתבונן בקוד הבא:
for (int i: arr) {
System.out.println(i);
}
מה קורה? הלולאה עוברת איבר אחרי איבר במערך, מתחילתו ועד סופו, ומציבה את i
בתור ערך האיבר הנוכחי. בתוך הלולאה מודפס הערך של i
. קוד זה לגמרי זהה מבחינת ביצוע לקוד הבא:
for (int i=0; i<arr.length; i++) {
System.out.println(arr[i]);
}
על אף היתרונות שבשיטה זו, כגון בהירות הקוד, מתקיים בה החיסרון הגדול: אין גישה למספר הסידורי של האיבר, אלא רק לערך. בנוסף, לא קיימת אפשרות לדלג על איברים מסוימים.[2]
אם כך, באיזו לולאה כדאי להשתמש?
עריכהאין לולאה שטובה או מתאימה יותר באופן כללי. כדי לבחור את סוג הלולאה יש לבחון את הדרישות מהתוכנית. לפעמים נוח יותר לעבוד עם לולאת for, במיוחד כאשר מבצעים פעולה מספר מוגדר של פעמים - הדפסת לוח הכפל, מעבר על רשימה, וכדומה. קרוב לוודאי שלולאת while תתאים יותר אם נרצה לחזור על פעולה מספר לא ידוע של פעמים, כמו - משחק שנגמר רק כאשר תנאי מסויים לא מתקיים עוד.
הפרק הקודם: מחרוזות |
תנאים ולולאות תרגילים |
הפרק הבא: הרחבה על משתנה בוליאני, ערך null |