הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

מבנים ששווה להכיר: List Comprehension

23/06/2020

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

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

את השם List Comprehension פגשתי לראשונה ב Python שם הקוד הבא לקח רשימה של מספרים והחזיר רשימה של ריבועיהם:

numbers = range(10)
squares = [i * i for i in numbers]

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

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

sum([i * i for i in range(10)])

(*) אני לא מדבר כאן עדיין על הרכבות מתוחכמות יותר כמו Generator Comprehension, למרות שאם אתם כותבים הרבה פייתון אתם כבר יודעים שאין טעם בסוגריים המרובעים בתוך העגולים, ואם לא - אז אולי נכתוב על זה פוסט ביום אחר.

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

Enum.sum(for n <- 0..9, do: n * n)

ו Clojure:

(apply + (for [i (range 10)] (* i i)))

ברוב השפות שאינן פונקציונאליות אין כתיב מובנה להרכבה אבל אנחנו משתמשים בפונקציות map ו filter כדי לקבל בדיוק את אותה תוצאה. כך ב Ruby:

(0..9).map {|i| i * i }.sum

ב JavaScript:

// some prep work
let numbers = new Array(10).fill(0).map((val, index) => index);
Array.prototype.sum = function() {
    return this.reduce((a, b) => a + b, 0)
}

numbers.map(i => i * i).sum()

אגב ב JavaScript הייתי צריך לממש לבד את sum ואת range שקיימות מהקופסא בשפות אחרות.

ואפילו Java כבר תומכת בתחביר דומה:

public class Main {

    public static void main(String[] args) {
      IntStream stream = IntStream.range(0, 10);
      System.out.println(stream.map(i -> i * i).sum());
    }
}

מרגע שהתחלנו לחשוב על הבעיה שלנו בתור רשימה שצריכה להפוך לרשימה אחרת, לא משנה באיזה שפה אנחנו מדברים אנחנו נוכל לייצג את המבנה - בדיוק כמו שהיינו מייצגים מבנה של משתנה, לולאה, תנאי או כל מבנה בסיסי אחר בשפה. ככל שנתרגל להשתמש במבנים של map, filter ו reduce כך יהיה לנו קל יותר לכתוב ולקרוא תוכניות במגוון שפות.

איך קוד מונחה עצמים הרס לי את החיים

22/06/2020

בקורס Python היום ניסינו לעשות מירוץ שליחים בגירסת המתכנתים - כל אחד קיבל את המקלדת ל-5 דקות וניסה לכתוב קוד כשכל האחרים צועקים כל מיני רמזים (שלפעמים עזרו ולפעמים רק הפריעו). היה כיף ובסוף הצלחנו לפתור יחד תרגיל מ Advent Of Code, והפוסט היום ידבר על אחת השאלות שעלו בדרך, ועל ההבדל בין ביצועים שרואים לבין ביצועים אמיתיים. אבל קודם התרגיל.

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

הכיוון הראשון שעבד נראה כך:

input_range = range(145852, 616942)
total = 0

for candidate in input_range:
    flag_all_up = True
    flag_same = False
    cand_str = str(candidate)
    for i in range(5):
        if cand_str[i] == cand_str[i+1]:
            flag_same = True
        elif cand_str[i] > cand_str[i+1]:
            flag_all_up = False
            break

    if flag_all_up and flag_same:
        total += 1

print(total)

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

def all_up(candidate):
    cand_str = str(candidate)
    for i in range(5):
        if cand_str[i] > cand_str[i+1]:
            return False

    return True

def has_two_adjacent_digits(candidate):
    cand_str = str(candidate)
    for i in range(5):
        if cand_str[i] == cand_str[i+1]:
            return True
    return False

total = sum(
        map(lambda candidate: all_up(candidate) and has_two_adjacent_digits(candidate),
            input_range))
print(total)

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

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

class TwoAdjacentDigitsPredicate:
    def __init__(self):
        self.result = False

    def process(self, a, b):
        if a == b:
            self.result = True

class AllUpPredicate:
    def __init__(self):
        self.result = True

    def process(self, a, b):
        if a > b:
            self.result = False

def apply_predicates(val, *predicates):
    val = str(val)
    for i in range(5):
        for p in predicates:
            p.process(val[i], val[i+1])
    return all([p.result for p in predicates])

total = filter(lambda candidate: apply_predicates(
            candidate,
            TwoAdjacentDigitsPredicate(), AllUpPredicate()), input_range)

print(sum(1 if x else 0 for x in total))

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

יש לכם כבר ניחוש מה זמני הריצה לכל גירסא? הנה זה בא:

# First version - single function
python3 aoc.py  0.81s user 0.02s system 85% cpu 0.965 total
# Second version - multiple functions
python3 aoc.py  0.54s user 0.01s system 97% cpu 0.568 total
# Third version - Object Oriented
python3 aoc.py  2.52s user 0.04s system 83% cpu 3.072 total

אז כן כצפוי גירסת ה Object Oriented היא האיטית ביותר. ליצור אוביקט חדש כל פעם זו פעולה יקרה ובזבזנית. מפתיע לראות שהדבר שחשבנו שיאט אותנו (הרצה הלולאה של 5 איטרציות פעמיים) בכלל לא הפריע לפייתון והגירסא השניה היא גם הנקיה ביותר וגם המהירה ביותר.

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

והמסקנה? בביצועים כמו בביצועים כדאי לבדוק כל דבר. תכנות מונחה עצמים כמעט תמיד יאט אתכם (אבל לפעמים הוא שווה את המחיר) ובכל מקרה מומלץ לא ליצור יותר מדי אוביקטים.

איך לשלוח הודעת ווטסאפ מ Python

21/06/2020

בזכות twilio שליחת הודעות sms וגם ווטסאפ מתוך תוכנת מחשב מעולם לא היתה פשוטה יותר. בואו נראה תוכנית Python פשוטה איתה שלחתי לעצמי הודעת ווטסאפ. קודם הקוד, אחרי זה הסבר עליו ובסוף הרחבות ומחירים.

המשך קריאה

איפה מוצאים זמן?

20/06/2020

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

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

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

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

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

ואם אתם רוצים עזרה ביצירת הסביבה שבה יהיה לכם קל יותר ללמוד - תשמחו לשמוע שזה בדיוק מה שאנחנו עושים ב Developer Bootcamps כאן באתר. מסלול הלימוד ב Bootcamp מיועד למתכנתים עסוקים מהתעשיה, מבוסס על השקעה של שעה ביום בממוצע ויוצר עבורכם סביבת עבודה שבה ללמוד ולהתקדם יהיה הדבר הקל שתרצו לעשות. למידע על הקורסים הבאים שיפתחו בקרו אותנו בדפי הקורסים:

קורס Python במסלול Developer Bootcamp

קוד React במסלול Developer Bootcamp

סמנו ביומנים: מחזור שני של הבוטקמפ למתכנתים של טוקוד ייפתח ביולי

19/06/2020

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

היום פתחתי את ההרשמה למחזור השני של בוטקמפ המפתחים של ToCode לקורסי React ו Python. זה מה שהולך לקרות בהם:

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

  2. אתם תתאמצו לפתור תרגילים מעניינים ותקבלו פידבק בונה על כל קוד שתגישו. הפידבק הוא אישי ויעזור לכם להבין איך להשתפר בעבודה הספציפית שלכם.

  3. אתם תראו את העבודה של חבריכם לקבוצה במפגשי ה Live ותלמדו מהם ומהמנטור Best Practices לפיתוח.

  4. אתם תציגו את העבודה שלכם מול אנשים אחרים ותתאמנו על לענות על שאלות טכניות על הקוד שבחרתם לכתוב, שאלות שיעזרו לכם להבין טוב יותר את הטכנולוגיה והקוד.

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

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

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

לפרטים נוספים - קורס פייתון במסלול בוטקמפ למתכנתים: https://tocode.ravpage.co.il/python.

קורס React במסלול בוטקמפ למתכנתים: https://tocode.ravpage.co.il/react.

מחסומי תרבות

18/06/2020

האנשים היחידים שנכנסים לאתר הזה הם אנשים שמדברים עברית. אפילו שגוגל טרנסלייט יודע לקחת כל אחד מהפוסטים שלי ולתרגם אותו לכל שפה, אף אחד לא באמת הולך להפעיל גוגל טרנסלייט בשביל לקרוא פוסטים בשפות זרות (חוץ מהפעם ההיא שאתה תקוע על הודעת שגיאה לא ברורה והמקום היחיד שהיא עולה בחיפוש גוגל זה באיזה פוסט ביפנית. אבל זה החריג וזה באמת אירוע נדיר).

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

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

כמה חומרי לימוד מעניינים הפסדתם כי אתם לא מכירים Linux? או Windows? או להריץ משורת הפקודה? או איך עובדת הצפנה? או מה זה תכנות פונקציונאלי?

אחת הדרכים לקבל פרספקטיבה על מה שאנחנו מפסידים היא לקפוץ ל Hacker News ולבדוק - כמה מבין 30 המאמרים בעמוד הראשון הם דברים שאני יכול להתחבר אליהם ולהבין אותם? ועבור אלה שלא, מה חסר לי בשביל שאהיה מסוגל לקרוא ולהבין?

סרביסים של אחרים

17/06/2020

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

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

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

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

הסתמכות על אחרים היא בלתי נמנעת, ועדיין שווה תמיד לוודא שיש לכם Fallback מתאים באזורי הליבה של המוצר שלכם.

שלום עולם ב Python עם טוויסט

16/06/2020

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

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

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

הנה התוכנית ב-4 שורות.

from gtts import gTTS

google_voice = gTTS(text="Hello world", lang='en')
google_voice.save('sound.mp3')

לפני שנעבור על הקוד שורה אחרי שורה קחו שתי דקות להקשיב לו. הכנסו לקישור: https://repl.it/@ynonp/AjarFatalCallbacks#main.py

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

בתוך הרפליט תוכלו ללחוץ על כפתור Run כדי להריץ את התוכנית, ואז בצד שמאל תראו שנוצר קובץ חדש בשם sound.mp3. לחצו עליו ותשמעו את גוגל אומר Hello world. ואפשר גם לשחק עם הקוד: שנו את הטקסט בתוכנית מ Hello world לכל משפט אחר, הריצו מחדש ותוכלו לשמוע את גוגל אומר את המשפט החדש.

המשך קריאה

לבנות לעצמך את הסביבה

15/06/2020

מאיזושהי סיבה אחד הדברים שהכי מטרידים מתכנתים זו סביבת העבודה -

״באיזה IDE צריך לכתוב?״

״איך מתקינים את המערכת?״

״איך מקמפלים?״

״איך מריצים את הבדיקות?״

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

במקום לחפש איך "צריך" לעבוד, מומלץ יותר להסתכל על זה כמו שופינג ולחפש איזה סוגי פיתרונות אני אוהב. האם נוח לי לעבוד ב Windows או Linux? או אולי בכלל Docker? האם Visual Studio Code גורם לי להרגיש בבית או שאני מעדיף IDE יותר ספציפי. וטיפ טוב נוסף הוא לא להרגיש רע לנסות כל מיני דברים ולהחליף. פעם אחת ליצור פרויקט עם create-react-app, ואחרי זה לנסות ליצור פרויקט מאפס עם webpack ואולי פעם שלישית לבחור טמפלייט ממקום אחר.

ככל שננסה יותר אפשרויות נוכל להסתכל על הקמת הסביבה בתור חווית איסוף בה אנחנו לוקחים קצת מכאן וקצת משם ומחברים את הכל לסביבה אישית שמתאימה לנו.

דוגמת ריפקטורינג של טפסים ב React

14/06/2020

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

המשך קריאה