שלום אורח התחבר

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

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

מעדיפים לקרוא מהטלגרם? בקרו אותנו ב:@tocodeil

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

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

def double(arr):
    result = []
    for item in arr:
        result.append(item * 2)

    return result
def 

ולעומתה:

def mymap(f, arr):
    result = []
    for item in arr:
        result.append(f(item))

    return result

def double(arr):
    return mymap(lambda x: x * 2, arr)

וזו:

def double(arr):
    return map(lambda x: x * 2, arr)

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

המשך קריאה...

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

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

ואז התחלתי לחשוב-

אולי אני יכול לקצר זמנים? מה אם אקשיב ל-3 פרקים ביום? או אכוון את המהירות לכפול 2 ואקשיב ל-6 פרקים ביום? אם אצליח לסיים להקשיב לכל 100 הפרקים של הפודקסט בשלושה ימים, האם אוכל לקבל את אותה דחיפה קדימה, כמו בתוכנית הפרק ביום המקורית שלי?

כמובן שלא.

שני דברים חשובים הולכים לאיבוד בגירסת הלמידה בהילוך מהיר:

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

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

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

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

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

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

יש משהו בלהיות בתוך הפרוסס הזה של כתיבה שנותן את ה Context לכל המסביב. וברגע שיש Context ומוטיבציה כל השאר רץ מעצמו.

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

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

ומה הבעיה עם זה שהומברו יתקין את pkg-config אתם שואלים? נו זה ברור, ה pkg-config שהומברו מתקין יכול להחזיר מידע רק על חבילות שהומברו התקין. אז עכשיו אנחנו יכולים לנסות לבנות תוכנה שצריכה איזו חבילה (נגיד libdbus-1-dev), ואפילו שהחבילה מותקנת דרך apt-get אי אפשר יהיה לבנות את התוכנה שלנו כי הבניה מפעילה את pkg-config שמחפש רק בספריות שהותקנו דרך הומברו.

אחרי שמבינים מה לחפש גם הפיתרון די פשוט ומופיע ב Issue הזה. כל מה שצריך זה להסיר את pkg-config מהומברו וכך חוזרים לגירסה של המערכת:

brew unlink pkg-config

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

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

  1. איזה עוד משפטים אפשר להגיד עם good ?

  2. ואיזה עוד משפטים יש עם המילה morning ?

  3. והאם יש דברים אחרים שאפשר להגיד לחברים בבוקר?

  4. ומה אומרים בצהריים? ובערב? ולפני השינה?

  5. ומה מקור המילה good ? באיזה עוד שפות היא מופיעה?

  6. ומה לגבי morning? ומה זה בכלל morrow? ואיך זה קשור ל tomorrow?

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

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

function createBox(props) {
    const style = {
        display: 'inline-block',
        border: '1px solid',
        width: '80px',
        height: '80px',
        verticalAlign: 'top',
        ...props.style,
    };

    return (
        <div style={style} {...props} />
    );
}

function App() {
    return (
        <div>
            {_.range(10).map(() => createBox())}
        </div>
    );
}

הפונקציה App היא אכן קומפוננטה ומחזירה מערך של 10 div-ים עם עיצוב מתאים. הפונקציה createDiv יותר בעייתית: היא אינה קומפוננטה אבל היא נראית קצת כמו והיא מסבכת אותנו כשצריך לעשות Refactor. אם בעתיד נצטרך לשמור סטייט בתוך הקופסאות האלה ונרצה להוסיף קריאה ל useState לתוך הפונקציה, אז הסטייט יישמר בקומפוננטה שקוראת לפונקציה כלומר ב App. בגלל שהפונקציה לא נראית כמו Custom Hook קל להתבלבל ולהפעיל אותה מתוך תנאי או לולאה מה שיגרום לתוצאות לא צפויות.

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

function Box(props) {
    const style = {
        display: 'inline-block',
        border: '1px solid',
        width: '80px',
        height: '80px',
        verticalAlign: 'top',
        ...props.style,
    };

    return (
        <div style={style} {...props} />
    );
}

function App() {
    return (
        <div>
            {_.range(10).map(() => <Box />)}
        </div>
    );
}

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

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

המשך קריאה...

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

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

הסיבה השניה היא מנגנון שנקרא Language Server Protocol או בקיצור lsp. הרעיון הוא ליצור מנגנון סטנדרטי של השלמה אוטומטית לעורכי טקסט כך ש"שרת" ההשלמה האוטומטית יהיה חיצוני לעורך ושני הרכיבים יתקשרו באמצעות פרוטוקול הודעות. coc מתקשר עם שרת שפות כדי להציע את ההשלמה האוטומטית אז אם יש לנו שרת שפות טוב ומתוחזק גם הצעות ההשלמה יהיו טובות. הרצת שרת שפות מתוך העורך לוקחת זיכרון ולפחות אצלי על המכונה צריכת הזיכרון של vim היתה גבוהה משמעותית כשהוספתי את coc בחלק מהשפות. בדף הזה יש רשימת של כל שרתי השפות הידועים לאדם: https://microsoft.github.io/language-server-protocol/implementors/servers/.

בשפות שניסיתי בינתיים ההשלמה היתה מדויקת ומהירה ולכן אם אתם בעניין השלמות אוטומטיות אני מאוד ממליץ להתקין את התוסף. הוראות התקנה תמצאו בדף התוסף כאן: https://github.com/neoclide/coc.nvim.

אחרי ההתקנה יש להפעיל מתוך vim פקודות להתקנת שרת השפות (או שרתי השפות) שאתם צריכים, למשל בשביל לעבוד עם typescript הייתי צריך להפעיל מתוך וים את:

:CocInstall coc-tsserver

ואלה השורות שהוספתי ל vimrc כדי לקבל התנהגות יותר אינטואיטיבית:

let g:lsc_auto_map = v:true

nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)

inoremap <silent><expr> <c-@> coc#refresh()
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
                              \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

" Use K to show documentation in preview window.
nnoremap <silent> K :call <SID>show_documentation()<CR>

function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  elseif (coc#rpc#ready())
    call CocActionAsync('doHover')
  else
    execute '!' . &keywordprg . " " . expand('<cword>')
  endif
endfunction

" Symbol renaming.
nmap <leader>rn <Plug>(coc-rename)

" Formatting selected code.
xmap <leader>f  <Plug>(coc-format-selected)
nmap <leader>f  <Plug>(coc-format-selected)

" Add `:Format` command to format current buffer.
command! -nargs=0 Format :call CocAction('format')

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

המשך קריאה...

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

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

  1. ללמוד איך בנויה אפליקציית Web (כולל HTML, CSS וקצת JavaScript)

  2. ללמוד איך עובד קוד צד שרת ומה הקשר בין Front End ל Back End

  3. ללמוד על פרוטוקולי תקשורת ובמיוחד HTTP

  4. ללמוד שפת תכנות כלשהי איתה אכתוב את הבדיקות

  5. ללמוד איך להשתמש ב Selenium ולכתוב בדיקות בשפת התכנות שבחרתי

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

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

  1. לכתוב ולהריץ תוכנית Hello World בשפת פייתון.

  2. לכתוב תוכנית בשפת Python שמדפיסה את כל המספרים הזוגיים.

  3. לכתוב דף אינטרנט שמציג מידע עליי.

  4. לכתוב תוכנית פייתון שמושכת מידע משרת מרוחק.

  5. לכתוב תוכנית פייתון שמפעילה דפדפן ובודקת מה הטקסט שמופיע בכותרת של הדף.

  6. להריץ את התוכנית שכתבתי בצורה אוטומטית בתור משימה מתוזמנת.

  7. לקבל אימייל מהתוכנית בסוף ההרצה אם הטקסט הוא לא מה שחשבתי.

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

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