ch
Feedback
ToCode

ToCode

前往频道在 Telegram

טיפים קצרים למתכנתים מאת ינון פרק

显示更多
1 419
订阅者
无数据24 小时
无数据7
无数据30
帖子存档
ToCode
1 419
# אם הייתי מתחיל היום פרויקט SaaS חדש בפוסט ארוך שפורסם הבוקר בהאקרניוז שיתף היזם סבסטיאן דובואה את החוויה שלו מפיתוח מוצר בשנתיים האחרונות. הסיפור בקצרה הוא שסבסטיאן עובד כבר תקופה ארוכה על המוצר אבל עדיין לא הצליח להגיע למוצר עובד ואין מה לדבר על התחלת השיווק. חוץ מהבעיה של Scope גדול מדי לפרויקט עצמו קשה לקרוא את הפוסט בלי לחשוב על כמות העבודה שסבסטיאן השקיע בשימוש בטכנולוגיה מודרנית. בין השאר הוא מספר על שלושה חודשים של השקעה בפיתוח Build System ו CI, ובהמשך גם על השימוש ב Kubernetes ואפילו השקעה של 3,000 אירו ב UX/UI. כל זה לפני שיש לקוח משלם ראשון. הסיפור של סבסטיאן ממחיש בעיה שהרבה מתכנתים נתקלים בה כשמגיעים לכתוב פרויקט לבד: הטכנולוגיה שאני צריך בשביל לבנות פרויקט כמתכנת יחיד מאפס שונה מאוד מהטכנולוגיה שחברה גדולה צריכה בשביל להצליח. בתור מתכנת יחיד (או צוות קטן), אין לנו כמעט בעיות של סינכרון ושל אמון. מצד שני מה שכן נמצא במחסור הוא שעות פיתוח. אפשר לדמות את הסיטואציה למכונית שנוסעת על מיכל דלק כמעט ריק וחייבת להגיע לתחנת דלק לפני שייגמר המיכל. רוב הנסיעות בעולם אינן כאלה, ובנסיעה כזאת יש לנו אילוצים אחרים. במקרה של SaaS שמפותח כ Bootstrap (יזם יחיד או צוות קטן) האילוץ שלנו הוא להגיע לדולר הראשון כמה שיותר מהר, לפני שהצוות יעלם לפרויקטים אחרים שכן מכניסים כסף. הדרך להגיע לשם היא להשתמש בכמה שיותר קיצורים. זה נשמע זוועה אבל אם אני מתחיל מחר פרויקט SaaS זה הסטאק הטכנולוגי שאני אבחר: 1. פריימוורק פיתוח ווב כמה שיותר מלא - לדוגמה Rails או Django. 2. בלי פריימוורק צד לקוח, מקסימום jQuery. 3. מבנה Monolith, בלי Micro Services. 4. קונה Theme מוכן מהרשת, מוכן לעשות קצת התאמות. 5. מעלה קוד לשרת עם rsync. כלל האצבע כאן הוא שאנשים ירשמו למוצר SaaS שלך אם המוצר פותר להם בעיה, בלי קשר לטכנולוגיה שמאחורי המוצר. ואחרי שהם ירשמו יהיה לכם הרבה יותר קל להחליף את מנגנון ה Deployment, כשתדעו שיש לכם ביד מוצר ששווה את ההשקעה שלכם.

ToCode
1 419
/daily

ToCode
1 419
# טיפ נאמפיי: החזרת אינדקסים מפונקציה בעבודה עם נאמפיי אחד הדברים הראשונים שנתקלים בהם הוא תחביר הסלייסים כדי לגשת לאינדקסים מרובים במערך. לדוגמה אם ניקח את לוח הכפל:
import numpy as np

arr = np.arange(1, 11) * np.arange(1, 11).reshape(-1, 1)
אז אפשר לקחת רק את הסלייס של 9 הערכים בצד שמאל למעלה עם הפקודה:
In [6]: arr[0:3,0:3]
Out[6]: 
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])
אז זה קול והכל אבל מה קורה כשאנחנו רוצים להיות יותר יצירתיים, למשל אם נרצה לבנות פונקציה שתחזיר את הסלייס המתאים לפי כל מיני פרמטרים? הכי אי אפשר לכתוב:
def get_index():
    # THIS DOES NOT WORK
    return 0:3,0:3
    
print(arr[get_index()])
וזה מביא אותנו לטיפ היומי - הפונקציה המתאימה במקרה כזה נקראת slice ואני משתמש בה באופן הבא:
In [11]: def get_index():
    ...:     return (slice(0, 3), slice(0, 3))

In [12]: arr[get_index()]
Out[12]: 
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])
בצורה כזאת קל לקחת לוגיקה שחוזרת על עצמה לחישוב הסלייס מהמערך, לכתוב אותה במקום אחד ולהשתמש בה בכל מקום שצריך.

ToCode
1 419
# טיפ נאמפיי: החזרת אינדקסים מפונקציה בעבודה עם נאמפיי אחד הדברים הראשונים שנתקלים בהם הוא תחביר הסלייסים כדי לגשת לאינדקסים מרובים במערך. לדוגמה אם ניקח את לוח הכפל:
import numpy as np

arr = np.arange(1, 11) * np.arange(1, 11).reshape(-1, 1)
אז אפשר לקחת רק את הסלייס של 9 הערכים בצד שמאל למעלה עם הפקודה:
In [6]: arr[0:3,0:3]
Out[6]: 
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])
אז זה קול והכל אבל מה קורה כשאנחנו רוצים להיות יותר יצירתיים, למשל אם נרצה לבנות פונקציה שתחזיר את הסלייס המתאים לפי כל מיני פרמטרים? הכי אי אפשר לכתוב:
def get_index():
    # THIS DOES NOT WORK
    return 0:3,0:3
    
print(arr[get_index()])
וזה מביא אותנו לטיפ היומי - הפונקציה המתאימה במקרה כזה נקראת slice ואני משתמש בה באופן הבא:
In [11]: def get_index():
    ...:     return (slice(0, 3), slice(0, 3))

In [12]: arr[get_index()]
Out[12]: 
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])
בצורה כזאת קל לקחת לוגיקה שחוזרת על עצמה לחישוב הסלייס מהמערך, לכתוב אותה במקום אחד ולהשתמש בה בכל מקום שצריך.

ToCode
1 419
# מה בננה אני מודה שקצת הופתעתי כשערן פירסם בקבוצת טלגרם של הבלוג ברכת שנה טובה בצירוף קוד ה JavaScript הבא שמדפיס את המילה banana:
console.log(('b'+'a'+ +'a'+'a').toLowerCase());
קודם כל כי בטלגרם השורות קצרות יותר והיה קשה לראות את הרצף של שני הפלוסים (השורה נחתכה בדיוק ברווח ביניהם), אבל יותר מזה בגלל שבאמת אין n בשום מקום בטקסט המקורי. ... אז מה קרה כאן? ... המפתח לתעלומה הוא העובדה שב JavaScript אופרטור הפלוס האונארי (Unary Plus) הוא דרך מתוחכמת לבצע המרה למספר. בגירסה פחות מסתורית של הקוד אפשר לכתוב כך:
console.log(('b' + 'a' + Number('a') + 'a').toLowerCase());
וזה כבר מתחיל להיות הגיוני כי ב JavaScript המרה של המחרוזת a למספר מחזירה את הערך המיוחד NaN שמשמעותו כמו שמו היא פשוט Not A Number (הגיוני, כי a אינו מספר):
> Number('a')
NaN
עכשיו נשאר רק להמיר את NaN למחרוזת ולהפוך לאותיות קטנות, והנה ה n-ים לבננה שלכם.

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

ToCode
1 419
# הגיע הזמן להפסיק להשתמש ב Debugger אחד הכלים הפופולריים בארגז הכלים של מתכנתים רבים הוא ה Debugger - זו החיפושית האגדית שמאפשרת לי להיכנס לקוד בזמן אמת, לטייל בתוך המערכת ולהציץ מה מצב כל אחד מהחלקים מבפנים כדי להבין איפה הטעות. אבל שימוש יתר ב Debugger עלול לאורך זמן להפוך אתכם למתכנתים פחות טובים ובמקום לעזור לכם לפתור באגים רק מייצר ויותר באגים. בואו נצלול יחד לאחד הנושאים השנויים במחלוקת בעולם הפיתוח ונראה כמה שיטות עבודה טובות יותר. ## למה אני משתמש ב Debugger? בתור מפתח Front End אני משתמש ב Debugger של הדפדפן כדי "להיכנס" לתוך המערכת. נניח שיש לי את הפונקציה הבאה:
function handleClick(ev) {
    const xhr = new XMLHttpRequest();
    xhr.open('POST, '/form.php');
    xhr.addEventListener('load', onLoad);
    xhr.send({name: 'ynon'});
}
הפונקציה לא עובדת ואני לא מבין למה. אני יכול להוסיף נקודת עצירה באחת השורות, לעבור שורה-שורה בקוד הפונקציה ולראות האם כל שורה בנפרד מצליחה או זורקת שגיאה. בנוסף, אם גיליתי את השורה הבעייתית אני יכול מתוך ה Debugger להפעיל אותה בגירסאות שונות עד שאני מגיע לגירסה הנכונה. סיטואציה נוספת בה אני משתמש לעתים קרובות ב Debugger היא במצבים שאני לא יודע מה הוביל לקריאה מסוימת. נניח שיש לי את הפונקציה הזאת ביישום:
function onReady(data) {
    const el = document.querySelector('.info');
    el.querySelector('name').textContent = data.name;
}
ואני רוצה לכתוב מידע נוסף לתוך האלמנט .info מתוך האוביקט data, אבל אולי אני לא יודע איזה שדות ומידע שמורים ב data או איך בדיוק נקרא השדה שאני מחפש. הרבה פעמים קל לי להוסיף נקודת עצירה בתוך הפונקציה, להדפיס מתוך ה Debugger את האוביקט data ולראות את שמות השדות שבו. ## מה הבעיה עם ה Debugger בדוגמה הראשונה? בדוגמת הקוד הראשונה לשימוש ב Debugger הפעלתי אותו כדי למצוא טעות בפונקציה שידעתי שלא עובדת. הבעיה שלי היתה שאחת הפקודות שרשמתי עושה משהו אחר ממה שחשבתי שהיא צריכה לעשות, או מצפה להפעלה בממשק שונה מזה שאני בחרתי בו. הבחירה ב Debugger כדי למצוא את הבעיה היא בעייתית כי בתוך ה Debugger אני צריך לדעת על מה להסתכל. כנראה אצליח איתו למצוא את הבעיה הספציפית בקוד הזה, אבל הוא לא יעזור לי לתקן את המבנה המבלבל שהוביל לבעיה ולכן באגים נוספים באותו סגנון צפויים לחזור במקומות אחרים במערכת. השימוש ב Debugger יעודד אותי לייצר פיתרון נקודתי כי זה הדבר הקל לעשות מרגע שהבנתי את הבעיה. ## מה הבעיה עם ה Debugger בדוגמה השניה? בדוגמה השניה השתמשתי ב Debugger כדי להתמודד עם קוד מסובך מדי. הפונקציה אולי נראית פשוטה אבל שרשרת הקריאות שהובילה אליה כנראה לא. הפעלת Debugger כדי להבין איזה שדות יש לאוביקט בתוך פונקציה מסוימת היא בעייתית כי יכול להיות שהפונקציה תיקרא בהקשרים שונים ובכל הקשר האוביקט data יכיל שדות אחרים. יותר מזה, העובדה שאני כמתכנת לא יכול להסתכל במקום אחד ולגלות מהקוד מה הוביל להפעלה של פונקציה מסוימת ואיזה שדות אמורים להיות בפרמטר שהיא קיבלה מעידה משהו על המערכת ומבנה הקוד. כשאני מתרגל להחליק מצבים כאלה באמצעות Debugger, אני מעודד את עצמי ואת החברים שעובדים איתי על הפרויקט לכתוב קוד יותר מסובך. ## מה אפשר לעשות במקום להיכנס ל Debugger? קיימים מספר כלים טובים יותר מ Debugger שעוזרים לנו להבין ולכתוב קוד נקי יותר: 1. ארגון מחדש של הקוד כדי שיהיה קל לראות "מה מוביל למה" 2. שימוש ב Type Hints (בעולם ה Front End זה אומר שפה כמו TypeScript) כדי להיות מסוגלים בזמן כתיבת הקוד לדעת בוודאות מה המבנה כל אחד מהמשתנים שלנו. 3. כתיבת בדיקות יחידה שיעזרו לנו להוכיח לעצמנו מה כל קטע קוד באמת עושה. הפיתרונות האלה היו עוזרים לשתי הדוגמאות שהראיתי בפוסט ולמצבים רבים נוספים. בדוגמה הראשונה שימוש בספריה (או ארגון הקוד שלי כמו ספריה) ששולחת מידע ב Ajax לשרת היה מונע את הטעות בשליחת אוביקט במקום טקסט (שמובילה לשליחת המחרוזת Object object במקום ה JSON שרציתי להעביר לשרת). בדוגמה השניה שימוש ב TypeScript היה מאפשר לי לדעת, כבר בזמן כתיבת הקוד, איזה שדות יש באוביקט ואולי גם לקבל מבט טוב יותר על שרשרת הקריאות שהובילה לשם. ## מה אני מרוויח כשאני מוותר על ה Debugger? הוויתור על ה Debugger נראה בהתחלה כמו עיסקה לא משתלמת: אני מוותר על כלי עבודה שאני רגיל אליו בשביל משהו שאני פחות מכיר ושנראה כמו הרבה יותר עבודה. אני מקווה שהדוגמאות והפוסט הזה יעזרו לכם לראות שהמציאות קצת שונה:

ToCode
1 419
# לא חשבתי שאפשר כשחיפשתי את העבודה הראשונה שלי אחרי שהשתחררתי מהצבא ידעתי שאני אוהב את העולם של קורסים מקצועיים אבל לא העליתי בדעתי שאני יכול לעבוד בזה. לקח לי כמה שנים עד שבכלל ניסיתי להתקבל לעבודה במכללה טכנולוגית. לפני שנכנסתי לעולם פיתוח Mobile לא חשבתי שאפשר ללמוד לבד טכנולוגיה עד לרמה של תעשיה. הייתי בטוח שהדרך היחידה למצוא עבודה היא להתקבל למשרת Junior בתחום שמעניין אותך ולהתקדם משם. כשהגעתי לראיון עבודה ראשון למשרת מפתח Mobile, אחרי כמעט שנה שישבתי לכתוב לבד אפליקציות מהבית, המראיינים היו בהלם לגלות מתכנת מנוסה בדיוק כמוהם שמדבר איתם באותה שפה. ויותר מהם גם אני הופתעתי לראות שזה הצליח. כשרק התחלתי לעבוד פרילאנס הייתי בטוח שאף אחד לא ירצה לעבוד איתי בצורה ישירה ושאני חייב לעבוד דרך חברות ייעוץ או הדרכה מפורסמות. זה לקח כמה שנים עד שהיה לי אומץ לעזוב את המתווכים ולהתחיל למכור לבד, וכמובן שהייתי בהלם כשגיליתי שלקוחות שמחים לעבוד איתי בצורה ישירה. היום אנחנו מתחילים את 2021 ושנה חדשה היא הזדמנות, הזדמנות לאתגר את עצמנו שוב ולנסות לשבור עוד תקרת זכוכית. מאחל לכולנו למצוא ולהצליח לרדוף אחרי חלומות חדשים שחשבנו שלא אפשריים.

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

ToCode
1 419
# מילים ומשפטים כשלומדים שפה (שפת תכנות או שפה רגילה) חשוב לשים לב ללמוד גם מילים וגם תבניות של משפטים, ובניגוד לאינטואיציה - קשה מאוד לבנות אחד מתוך השני. בגלל זה אנשים שלומדים לתכנת ונתקלים פעם ראשונה בלולאת for אולי מרגישים שהם מבינים מה הולך מסביב, אבל כשתבקש מהם לכתוב לולאת for מקוננת איבדת אותם וצריך להסביר הכל מההתחלה. בלולאת for מקוננת אין פקודה חדשה (זו אותה for פשוט עוד פעם ובתוך), אבל כן יש מבנה חדש. אותו דבר עם המבנה של רקורסיה שלא כולל פקודות חדשות ובכל זאת מהווה אתגר רציני למתכנתים בתחילת הדרך. מצד שני ברגע שלמדנו מבנה מסוים אנחנו יכולים להשתמש בו בשפות חדשות ככל שנלמד את המילים בהן. לכן תוכנית שמדפיסה את לוח הכפל תיראה מאוד דומה ב C, perl, ruby, python, JavaScript או כל שפה פרוצדורלית אחרת שתיקחו. ברגע שיש בשפה תמיכה בלולאות for עם משתנה רץ, יהיה לנו קל לבנות בה את לוח הכפל. וברגע שיש בשפה פונקציות אנחנו יכולים לבנות בה את הרקורסיה, גם אם התחביר של הגדרת פונקציה וקריאה לה יהיו שונים בין שפות. משמעות של מילה נגזרת מסך כל המשפטים בהם היא יכולה לקחת חלק, ולכן ככל שאנחנו לומדים יותר מבנים תכנותיים כך קל לנו יותר גם ללמוד פקודות חדשות בשפה.

ToCode
1 419
# אני יודע את זה אחד הרגעים החשובים בהתמודדות עם בעיה הוא הרגע בו אתה מבין - "אני יודע את זה!"; אני יודע את זה כי פתרתי בעיות דומות בעבר, כי זה בדיוק כמו הבעיה ההיא רק עם תוספת מהבעיה מלפני שבוע. "אני יודע את זה" הוא הרגע בו אפשר להפסיק לחפש ברשת כל מיני רעיונות יצירתיים איך להתמודד עם הבעיה ופשוט להתקדם עם הדבר שאנחנו יודעים עד שנגיע לפיתרון. אם מישהו יבקש ממני מחר למרכז div ב CSS, אני מתחיל עם "אני יודע את זה" ואז ממשיך ליצור את הפלקסבוקס ולמרכז את התוכן. גם אם חסר לי סינטקס, החיפוש ברשת כבר לא יהיה חיפוש כללי של "איך לכתוב תוכן באמצע העמוד באמצעות CSS" אלא משהו יותר ספציפי כמו "Flexbox reference" או אפילו "flexbox align items example". ואני יודע את זה הוא המפתח להערכות זמנים חכמות, כי הוא מאפשר להבדיל בין אי הוודאות של התכנות (החלק בו אנחנו מחפשים כל מיני כיוונים ולא ממש יודעים מה אנחנו עושים), לבין העבודה המלוכלכת של כתיבת הקוד. והחלק הקשה? זה יהיה לזהות מתי "אני יודע את זה" יהפוך ל "הייתי בטוח שאני יודע את זה ועכשיו אני מבין שטעיתי בגדול".

ToCode
1 419
מצד שני אנקונדה עובד בעולם שלו, עם החבילות שלו ונועל אותנו לסביבה שלו. מרגע שהתחלתי לעבוד עם Anaconda אני לא ארצה לשלב חבילות pip רגילות כדי לא לקלקל את ניהול התלויות, וכמובן שאצטרך להשתמש בכלים של אנקונדה כדי להעביר את הקוד שלי למכונות אחרות (ולכן גם שם צריך להתקין את אנקונדה). ההעדפה שלי, למרות שזה כרוך ביותר עבודה, היא לבחור בכלים הפתוחים pip, venv ו pyenv כדי לנהל את גירסאות הפייתון והחבילות אצלי על המכונה. בכל זאת שווה להכיר גם את קונדה ולבחור את הכלי שמתאים ביותר לפרויקט ולגישה שלכם.