לדלג לתוכן

2.3 בקרת זרימה ולולאות הרצאה

תנאים - conditionals

if, else if, else

ב-JS התנאים עובדים אותו דבר כמו בפייתון, אבל עם תחביר שונה:

// JavaScript
let age = 20;

if (age >= 18) {
    console.log("Adult");
} else if (age >= 13) {
    console.log("Teenager");
} else {
    console.log("Child");
}
# Python equivalent
age = 20

if age >= 18:
    print("Adult")
elif age >= 13:
    print("Teenager")
else:
    print("Child")

ההבדלים העיקריים:
- ב-JS התנאי חייב להיות בסוגריים ()
- ב-JS הבלוק מוגדר על ידי { } ולא על ידי הזחה
- ב-JS אין elif - כותבים else if (שתי מילים)
- ב-JS אין נקודתיים : בסוף שורת התנאי

דוגמה נוספת:

let temperature = 35;

if (temperature > 30) {
    console.log("Very hot!");
    console.log("Drink water!");
} else if (temperature > 20) {
    console.log("Nice weather");
} else if (temperature > 10) {
    console.log("A bit cold");
} else {
    console.log("Freezing!");
}

בלוק עם שורה אחת

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

if (age >= 18) console.log("Adult");

אבל לא מומלץ - תמיד עדיף להשתמש בסוגריים מסולסלים כדי למנוע טעויות.

תנאים מקוננים

let isLoggedIn = true;
let isAdmin = false;

if (isLoggedIn) {
    if (isAdmin) {
        console.log("Welcome, admin!");
    } else {
        console.log("Welcome, user!");
    }
} else {
    console.log("Please log in");
}

אותו דבר בפייתון:

# if is_logged_in:
#     if is_admin:
#         print("Welcome, admin!")
#     else:
#         print("Welcome, user!")
# else:
#     print("Please log in")


switch - משפט בחירה

switch הוא אלטרנטיבה ל-if/else if כשיש הרבה אפשרויות לערך אחד:

let day = "Monday";

switch (day) {
    case "Monday":
        console.log("Start of the week");
        break;
    case "Tuesday":
    case "Wednesday":
    case "Thursday":
        console.log("Middle of the week");
        break;
    case "Friday":
        console.log("Almost weekend!");
        break;
    case "Saturday":
    case "Sunday":
        console.log("Weekend!");
        break;
    default:
        console.log("Invalid day");
}
  • switch בודק את הערך של הביטוי מול כל case
  • חייבים לשים break אחרי כל case - אחרת הקוד ימשיך ל-case הבא (fall-through)
  • default הוא מה שקורה אם אף case לא מתאים (כמו else)
  • אפשר לקבץ cases כמו Tuesday/Wednesday/Thursday בדוגמה למעלה

בפייתון אין switch (עד גרסה 3.10 שהוסיפה match/case):

# Python 3.10+ equivalent
# match day:
#     case "Monday":
#         print("Start of the week")
#     case "Friday":
#         print("Almost weekend!")

fall-through - הטעות הכי נפוצה

let color = "red";

// WITHOUT break - bug!
switch (color) {
    case "red":
        console.log("Red");    // this runs
    case "green":
        console.log("Green");  // this ALSO runs! (fall-through)
    case "blue":
        console.log("Blue");   // this ALSO runs!
}
// prints: "Red", "Green", "Blue" - probably not what we wanted!
  • בלי break, הקוד "נופל" ל-case הבא ומריץ אותו גם
  • זה התנהגות מכוונת של השפה, אבל כמעט תמיד זו טעות
  • תמיד שימו break אלא אם כן אתם רוצים fall-through בכוונה (כמו קיבוץ cases)

אופרטור תלת-תנאי - ternary operator

ראינו את זה בשיעור הקודם, בואו נעמיק:

// basic syntax: condition ? valueIfTrue : valueIfFalse
let age = 20;
let status = age >= 18 ? "adult" : "minor";

אפשר להשתמש בזה בתוך console.log:

let score = 85;
console.log(`You ${score >= 60 ? "passed" : "failed"} the exam`);

אפשר לשרשר (אבל לא מומלץ - קשה לקריאה):

let grade = score >= 90 ? "A"
          : score >= 80 ? "B"
          : score >= 70 ? "C"
          : score >= 60 ? "D"
          : "F";

כלל אצבע: אם ה-ternary הופך למסובך, עדיף if/else רגיל. ternary טוב להשמות פשוטות.

בפייתון:

# status = "adult" if age >= 18 else "minor"


Guard Clauses - יציאה מוקדמת

טכניקה חשובה לכתיבת קוד נקי - במקום תנאים מקוננים, נבדוק את המקרים הפשוטים קודם ונצא:

// BAD - deeply nested
function processUser(user) {
    if (user !== null) {
        if (user.isActive) {
            if (user.age >= 18) {
                console.log("Processing user...");
                // actual logic here
            } else {
                console.log("User is too young");
            }
        } else {
            console.log("User is not active");
        }
    } else {
        console.log("No user provided");
    }
}

// GOOD - guard clauses
function processUser(user) {
    if (user === null) {
        console.log("No user provided");
        return;
    }

    if (!user.isActive) {
        console.log("User is not active");
        return;
    }

    if (user.age < 18) {
        console.log("User is too young");
        return;
    }

    console.log("Processing user...");
    // actual logic here - no nesting!
}
  • במקום לקנן תנאים, בודקים את המקרים הבעייתיים ויוצאים עם return
  • הקוד הראשי (ה-"happy path") נשאר בלי הזחה מיותרת
  • הקוד הרבה יותר קריא

לולאות - loops

for - הלולאה הקלאסית

for (let i = 0; i < 5; i++) {
    console.log(i);
}
// prints: 0, 1, 2, 3, 4

המבנה: for (initialization; condition; update)
- let i = 0 - אתחול: רץ פעם אחת בהתחלה
- i < 5 - תנאי: נבדק לפני כל סיבוב, הלולאה עוצרת כשזה false
- i++ - עדכון: רץ אחרי כל סיבוב

בפייתון:

# for i in range(5):
#     print(i)

הלולאה של JS יותר מפורשת אבל גם יותר גמישה:

// count backwards
for (let i = 10; i > 0; i--) {
    console.log(i);
}
// prints: 10, 9, 8, ..., 1

// count by 2
for (let i = 0; i <= 10; i += 2) {
    console.log(i);
}
// prints: 0, 2, 4, 6, 8, 10

בפייתון:

# for i in range(10, 0, -1):
#     print(i)

# for i in range(0, 11, 2):
#     print(i)

while - כל עוד

אותו דבר כמו בפייתון:

let count = 0;

while (count < 5) {
    console.log(count);
    count++;
}
// prints: 0, 1, 2, 3, 4
# Python equivalent
# count = 0
# while count < 5:
#     print(count)
#     count += 1

ההבדל: ב-JS התנאי בסוגריים () והבלוק ב-{ }.

do...while - לפחות פעם אחת

זו לולאה שלא קיימת בפייתון:

let input;

do {
    input = prompt("Enter a number greater than 10:");
    input = Number(input);
} while (input <= 10);

console.log("You entered:", input);
  • הבלוק רץ לפחות פעם אחת - גם אם התנאי שקרי מההתחלה
  • התנאי נבדק אחרי הריצה הראשונה
  • שימושי כשצריך לבצע פעולה לפחות פעם אחת (למשל בקשת קלט מהמשתמש)

ההבדל מ-while רגיל:

// while - might not run at all
let x = 100;
while (x < 10) {
    console.log(x); // never runs! condition is false from the start
    x++;
}

// do...while - always runs at least once
let y = 100;
do {
    console.log(y); // prints 100! runs once before checking condition
    y++;
} while (y < 10);

בפייתון אין do...while. המקבילה:

# while True:
#     input_val = input("Enter a number greater than 10: ")
#     input_val = int(input_val)
#     if input_val > 10:
#         break

for...of - איטרציה על ערכים

for...of עובר על ערכים של אובייקט iterable (מערך, מחרוזת וכו'):

const colors = ["red", "green", "blue"];

for (const color of colors) {
    console.log(color);
}
// prints: "red", "green", "blue"

זה מאוד דומה ל-for בפייתון:

# colors = ["red", "green", "blue"]
# for color in colors:
#     print(color)

עובד גם על מחרוזות:

const word = "Hello";

for (const char of word) {
    console.log(char);
}
// prints: "H", "e", "l", "l", "o"

# for char in "Hello":
#     print(char)

שימו לב שמשתמשים ב-const ולא let כי בכל סיבוב נוצר משתנה חדש.

for...in - איטרציה על מפתחות

for...in עובר על מפתחות (keys) של אובייקט:

const person = {
    name: "Alice",
    age: 25,
    city: "Tel Aviv"
};

for (const key in person) {
    console.log(`${key}: ${person[key]}`);
}
// prints:
// "name: Alice"
// "age: 25"
// "city: Tel Aviv"

בפייתון:

# person = {"name": "Alice", "age": 25, "city": "Tel Aviv"}
# for key in person:
#     print(f"{key}: {person[key]}")

אזהרה: אל תשתמשו ב-for...in על מערכים! זה עובר על האינדקסים כמחרוזות, לא על הערכים:

const fruits = ["apple", "banana", "cherry"];

// DON'T do this with arrays
for (const index in fruits) {
    console.log(index);       // "0", "1", "2" (strings, not numbers!)
    console.log(typeof index); // "string"
}

// DO this instead
for (const fruit of fruits) {
    console.log(fruit);       // "apple", "banana", "cherry"
}

סיכום: for...of מול for...in

for...of for...in
עובר על ערכים מפתחות
לשימוש עם מערכים, מחרוזות אובייקטים
דוגמה for (const x of [1,2,3]) for (const k in {a:1})
מקבילה בפייתון for x in list for k in dict

break ו-continue

בדיוק כמו בפייתון:

break - יציאה מהלולאה

for (let i = 0; i < 10; i++) {
    if (i === 5) {
        break; // exit the loop when i is 5
    }
    console.log(i);
}
// prints: 0, 1, 2, 3, 4

continue - דילוג לסיבוב הבא

for (let i = 0; i < 10; i++) {
    if (i % 2 === 0) {
        continue; // skip even numbers
    }
    console.log(i);
}
// prints: 1, 3, 5, 7, 9

שילוב בלולאת while

let num = 0;

while (true) {
    num++;

    if (num % 3 === 0) {
        continue; // skip multiples of 3
    }

    if (num > 10) {
        break; // stop at 10
    }

    console.log(num);
}
// prints: 1, 2, 4, 5, 7, 8, 10

תוויות - labeled statements

ב-JS אפשר לתת שם (label) ללולאה ולהשתמש ב-break/continue כדי לצאת מלולאה חיצונית:

outer: for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
        if (i === 1 && j === 1) {
            break outer; // breaks out of BOTH loops
        }
        console.log(`i=${i}, j=${j}`);
    }
}
// prints:
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0
  • outer: הוא label - שם שנותנים ללולאה
  • break outer יוצא מהלולאה שסומנה ב-outer:
  • בלי ה-label, break היה יוצא רק מהלולאה הפנימית

בפייתון אין labels ללולאות. הדרך המקובלת היא להשתמש בפונקציה עם return או בדגל (flag):

# Python alternative (no labels)
# found = False
# for i in range(3):
#     for j in range(3):
#         if i == 1 and j == 1:
#             found = True
#             break
#     if found:
#         break

זה שימוש נדיר - בדרך כלל אפשר לכתוב את הקוד בצורה אחרת בלי labels.


דוגמה מסכמת

נכתוב תוכנית שמנחשת מספר:

// number guessing game
const secretNumber = Math.floor(Math.random() * 100) + 1; // random 1-100
let attempts = 0;
let guessed = false;

while (!guessed) {
    let input = prompt("Guess a number between 1 and 100:");

    // guard clause - user cancelled
    if (input === null) {
        console.log("Game cancelled!");
        break;
    }

    let guess = Number(input);
    attempts++;

    // validate input
    if (isNaN(guess) || guess < 1 || guess > 100) {
        console.log("Please enter a valid number between 1 and 100");
        continue; // skip to next iteration
    }

    if (guess === secretNumber) {
        guessed = true;
        alert(`Correct! The number was ${secretNumber}. You got it in ${attempts} attempts!`);
    } else if (guess < secretNumber) {
        console.log("Too low! Try higher.");
    } else {
        console.log("Too high! Try lower.");
    }
}

// rate the player
switch (true) {
    case attempts <= 3:
        console.log("Amazing!");
        break;
    case attempts <= 7:
        console.log("Good job!");
        break;
    case attempts <= 10:
        console.log("Not bad");
        break;
    default:
        console.log("Keep practicing!");
}

הדוגמה משלבת: while, if/else if/else, break, continue, switch ו-guard clause.

סיכום

  • תנאים ב-JS: סוגריים () סביב התנאי, סוגריים מסולסלים { } סביב הבלוק, else if במקום elif
  • switch שימושי כשיש הרבה ערכים אפשריים - אל תשכחו break
  • הלולאה הקלאסית for עם שלושה חלקים: אתחול, תנאי, עדכון
  • while - כמו בפייתון, do...while - רץ לפחות פעם אחת (אין בפייתון)
  • for...of - עובר על ערכים (למערכים) - כמו for x in list בפייתון
  • for...in - עובר על מפתחות (לאובייקטים) - כמו for k in dict בפייתון
  • break ו-continue עובדים כמו בפייתון
  • guard clauses הופכים קוד מקונן לקוד שטוח וקריא