למה זה דומה

19/04/2020

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

for i in range(0, 10):
    print("{} Hello World".format(i))

כי זה נראה ממש דומה לקוד ה Java:

for (int i=0; i < 10; i++)
    System.out.println(String.format("%d Hello World", i));

אבל הדמיון הזה הוא רק בראש של מי שחדש ל Python -

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

  2. ב Java המשתנה i חי בתוך הלולאה בלבד, אבל ב Python הגדרנו משתנה שמוכר בכל הפונקציה.

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

for (int i=9; i >= 0; i--)
    System.out.println(String.format("%d Hello World", i));

ב Python ניסיון להפוך את סדר המספרים ב range כמובן נכשל ולא מדפיס כלום:

for i in range(10, 0):
    print("{} Hello World".format(i))

ככל שנלך רחוק יותר בחקירה שלנו נגלה שקוד Python עושה דבר דומה לקוד ה Java, אבל בדרך מאוד שונה. בגירסת ה Java אנחנו יודעים מה זה לשמור את המספר 0 במשתנה או לשנות את ערך המשתנה ב-1. בגירסת ה Python בשביל להבין מה קורה בלולאה אנחנו צריכים להבין ש range בכלל מחזירה אוביקט שיש לו פונקציה בשם __iter__, שמחזירה משהו שיש לו פונקציה בשם __next__, ושבצורה אוטומטית for מפעיל את __iter__, לוקח את הדבר שהחזירה ומפעיל עליו __next__ עד שמקבל את ה Exception המיוחד שאומר שנגמרו כל האיברים. כלומר אם אני מנסה מתוך ה REPL להבין למה ה range לא עבד זה עשוי להיות מבלבל. בכתיבה הראשונה שתי הפקודות מחזירות תוצאה דומה:

>>> range(0, 10)
range(0, 10)
>>> range(10, 0)
range(10, 0)
>>>

ורק בהמשך חקירה אני מצליח לראות את הבעיה:

>>> len(range(0, 10))
10

>>> len(range(10, 0))
0

וכמובן:

>>> g = range(0, 10)
>>> i = iter(g)
>>> next(i)
0
>>> next(i)
1

# But ...

>>> g = range(10, 0)
>>> i = iter(g)
>>> next(i)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

הדמיון ל Java התחיל מאוד יפה אבל הוא היה יותר בראש שלנו מאשר בשפה ובתבנית עצמה. תבנית for של Python מזכירה הרבה יותר את תבנית האיטרטורים של Java, למרות שבפייתון המימוש הרבה יותר אלגנטי. ככל שאנחנו לומדים יותר לעומק איך השפה עובדת אנחנו יכולים למצוא נקודות דימיון אמיתיות לשפות וטכנולוגיות אחרות, ובאמת להסיק ממקום אחד לגבי מקומות אחרים - אבל צריך תמיד לזכור שיש גם צד מבלבל לסיפור, ושלפני שאנחנו מכירים טוב שפה יהיו לנו הרבה מצבים שמשהו "מצלצל מוכר" ומזכיר משהו משפה אחרת, אבל זה רק נראה כמו דומה.