פייתון לא עושה לנו חיים קלים כשאנחנו רוצים לברוח החוצה ממעמקי לולאה מקוננת. בדוגמה הבאה אני מנסה למצוא ולהדפיס שלושה מספרים שסכומם יהיה 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
הדבר החשוב בכל אחת מהדרכים הוא היחס ללולאות מקוננות: החיים בפייתון פשוט יפים יותר בלעדיהן, ולכן הדרך לברוח מתוך לולאה מקוננת תהיה לוותר עליה.