איך לברוח מלולאה כפולה ב Python

31/12/2020

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

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

for i in values:
    for j in values:
        for k in values:
            if i != j and j != k and i + j + k == 2020:
                print(f"Found! {i}, {j}, {k}")
                break

זה עובד, אבל... כך נראית התוצאה:

Found! 289, 480, 1251
Found! 289, 1251, 480
Found! 480, 289, 1251
Found! 480, 1251, 289
Found! 1251, 289, 480
Found! 1251, 480, 289

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

מה בכל זאת אפשר לעשות? דרך קלה לברוח מלולאה מקוננת היא להפוך אותה ללולאה רגילה באמצעות yield. הקוד נראה כך:

def triplets(values):
    for i in values:
        for j in values:
            for k in values:
                if i != j and j != k:
                    yield(i, j, k)


for i, j, k in triplets(values):
    if i + j + k == 2020:
        print(f"Found! {i}, {j}, {k}")
        break

פה הייתי צריך לקחת את הלולאה החוצה ולהגדיר אותה בפונקציה נפרדת בתור Generator שזה קצת מעצבן, אבל אחרי שעשיתי את זה אני יכול להמשיך להתייחס ללולאה בתור לולאה "רגילה" בה break עובד.

הפונקציה itertools.product היא דרך גנרית לבנות כזה מנגנון ובצורה מקוצרת כך שהקוד הבא עובד מעולה:

import itertools
for i, j, k in itertools.product(values, values, values):
    if i != j != k and i + j + k == 2020:
        print(f"Found! {i}, {j}, {k}")
        break

ובמקרה הספציפי שלנו שאנחנו רוצים 3 מספרים שונים נוכל לקצר תהליכים צעד אחד נוסף באמצעות הפונקציה itertools.combinations שכבר דואגת להחזיר לנו רק שלשות של מספרים שונים:

for i, j, k in itertools.combinations(values, 3):
    if i + j + k == 2020:
        print(f"Found! {i}, {j}, {k}")
        break

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