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

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

הצצה אחורה בביטוי רגולארי בפייתון

02/05/2018

קבלו חידת ביטויים רגולריים פשוטה לפתוח את הבוקר: נתון הטקסט I am :the: walrus ואנחנו רוצים לפצל אותו לשני חלקים כך שהחלק הראשון כולל את כל מה שמופיע עד הנקודותיים השניים (כולל הנקודותיים) והחלק השני כל מה שבא אחרי הרווחים.

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

טוב אז ברור ש split רגיל של מחרוזת לא יעבוד כאן - split יבלע לי את הנקודותיים ואת הרווח שאחריהן:

text = 'I am :the:      walrus'
text.split(': ')
['I am :the', '     walrus']

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

import re
text = 'I am :the:      walrus'
re.split('(: )', text)
 ['I am :the', ': ', '     walrus']

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

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

import re
text = 'I am :the:      walrus'
re.split('(?<=:) ', text)

# or if you don't need the spaces before the walrus
re.split('(?<=:) +', text)
['I am :the:', 'walrus']

באופן כללי ובפרט בפייתון ארבעת סוגי ההצצות והסימנים שלהן הם:

  1. הצצה קדימה - Positive Lookahead מסומן באמצעות (?=foo). חיפוש המקום בקלט שאחריו מופיעה המילה foo.

  2. הצצה אחורה - Positive Lookbehind מסומן באמצעות (?<=foo). חיפוש המקום בקלט שלפניו מופיעה המילה foo.

  3. הצצה קדימה שלילית -Negative Lookahead מסומן באמצעות (?!foo). חיפוש מקום בקלט שאחריו לא מופיעה המילה foo.

  4. הצצה אחורה שלילית - Negative Lookbehind מסומן באמצעות (?<!foo) חיפוש המקום בקלט שלפניו לא מופיעה המילה foo.

לדוגמאות נוספות על Lookahead ו Lookbehind שווה להעיף מבט בקישור כאן:

href='http://www.rexegg.com/regex-lookaround

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

איך מרגישה הצלחה

30/04/2018

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

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

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

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

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

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

קריא, נכון או גם וגם?

29/04/2018

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

// prints: new world
console.log(wordsFromList('a brave new world', ['world', 'new']);

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

function wordsFromList(sentence, words) {
    const wordsObject = {};
    words.forEach(word => { wordsObject[word] = true; });

    return sentence.split(' ').filter(word => wordsObject[word]).join(' ');
}

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

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

function wordsFromList(sentence, words) {
    const wordsList = new WordsList(words);
    const breakableSentence = new Sentence(sentence);

    return breakableSentence.filterWords(word => wordsList.includes(word)).toString();
}

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

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

מהר יותר מאופניים

28/04/2018

את יכולה לרוץ מהר יותר מאופניים, אבל לא לאורך זמן.

בשביל באמת לתכנת מהר כדאי יותר:

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

אפשר לרוץ יותר מהר מאופניים (לפחות בחלק מהזמן), אבל הרבה יותר משתלם ללמוד לרכב.

האם אנחנו הולכים לקבל Pattern Match גם ב JavaScript (השראה)

27/04/2018

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

למי שלא מכירים אין קשר לביטויים רגולאריים ומדובר במנגנון switch/case מתוחכם שמסתכל פנימה לתוך אוביקטים. יש בהצעה דוגמא ואם אתם סקרנים לכו לקרוא כאן: https://github.com/tc39/proposal-pattern-matchin

מעניין המשפט משם שההצעה לוקחת השראה מפיצ'ר מקביל שקיים ב Rust, F#, Scala וכמובן אליקסיר.

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

דוגמא יותר אקטואלית היא Generator Functions. אלה כבר קיימים ב JavaScript כולל בגירסא אסינכרונית. כלומר היום אנחנו יכולים לכתוב את הקוד הבא ודפדפנים יריצו אותו:

// noprotect
// Note the * after "function"
let stop = false;

async function* asyncRandomNumbers() {
    // This is a web service that returns a random number
    const url = 'https://www.random.org/decimal-fractions/?num=1&dec=10&col=1&format=plain&rnd=new';

    while (true) {
      const response = await fetch(url);
      const text = await response.text();
      yield Number(text);
    }
  }

  const inp = document.querySelector('input');

  async function example() {
    for await (const number of asyncRandomNumbers()) {
      inp.value = number;
      if (stop) break;
    }
  }

  function startStop() {
      if (stop) {
        stop = false;
        example();
      } else {
        stop = true;
      }
  }

  document.querySelector('button').addEventListener('click', startStop);
  example();

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

  async function example() {
    for await (const number of asyncRandomNumbers()) {
      inp.value = number;
      if (stop) break;
    }
  }

איך לולאת for מצליחה לכתוב למסך בכל איטרציה? ואיך JavaScript מצליח לתפוס את הלחיצה על הכפתור כשהוא "באמצע" לולאת ה for?

ברור שאם אתם מכירים Generator Functions ממקומות אחרים אתם יודעים לענות שהלולאה הזאת בעצם מתורגמת לרצף של callbacks כאשר אחרי כל איטרציה המנוע חוזר ל Main Loop, וזו ממש לא לולאת for קלאסית.

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

נ.ב. שבוע הבא יעלה כאן קורס JavaScript ES6/7/8. שבו אני מלמד על כל הפיצ'רים האלה. יש למה לחכות.

איך לעקוף אפליה לפי גיל

26/04/2018

סקוט ג'ונסון העלה השבוע את קורות חיפוש העבודה שלו בחצי קיטור חצי הזדמנות לפרסם מוצר שפיתח. אני חושב שכדאי לכם לקרוא את הסיפור כאן: http://fuzzyblog.io/blog/jobhound/2018/04/24/ten-things-i-learned-from-a-job-hunt-for-a-senior-engineering-role.html

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

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

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

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

  3. התחדשות- כדאי שהטכנולוגיות בקורות החיים שלך יתאימו לטכנולוגיות שעובדים בהן בחברות. בשנים האחרונות יש הרבה פחות חברות שעובדות ב Ruby on Rails. עדיין יש עבודה בתחום והשפה מצוינת והכל (האתר הזה כתוב בריילס), אבל אם היום אני מפרסם קורות חיים שלי למשרת מתכנת ריילס לא יהיו המון הצעות פשוט כי סטארט-אפים עברו ל node.js. הניסיון שלכם בטכנולוגיות ישנות לא נתפס כמשהו בעל ערך.

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

יש לכם עוד רעיונות? אשמח לשמוע בתגובות או במייל.

עיצוב תוכנה בתנועה

25/04/2018

עבדתי על שתי מערכות מורכבות שנכתבו בגישת תכנות מונחה עצמים:

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

את המערכת השניה עיצב מתכנת שלא תיכנן על מערכת גדולה וקצת יצא לו בטעות. כל העיצוב היה בנוי על 2-3 מחלקות שנלקחו מאיזה פרויקט Hello World שפשוט יצא מפרופורציה והפך למיליוני שורות קוד כמעט בלי שינוי של מבנה המחלקות. פונקציות באורך מאות שורות קוד היו מחזה נפוץ.

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

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

מיתוסים על מיומנות

24/04/2018

למה מתכנתים מסוימים מצליחים למצוא עבודה תוך שניה (גם בלי ניסיון בטכנולוגיה הרלוונטית) ואחרים לא? למה מתכנתות מסוימות מצליחות ללמוד טכנולוגיה מסוימת מהר יותר מאחרות? למה מתכנתים מסוימים מצליחים לראות וליישם פתרונות מהר יותר מאחרים?

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

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

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

ולשאלות- האם אתם יודעים מה רמת המיומנות שלכם? יש לכם מושג מה האחרים בצוות שלכם חושבים על רמת המיומנות שלכם? והאם אתם מרגישים שרמת המיומנות שלכם השתפרה בשנים האחרונות (או התדרדרה)?

3 מטרות

23/04/2018

בואו נדבר על 3 מטרות של קורסים:

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

  2. הקניית ידע - הקורס מלמד איך להשתמש בכלי מסוים.

  3. שיפור מיומנות (טכניקה).

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

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

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

חדש מאתר ToCode: תוכנית המנויים

22/04/2018

תוכנית המנויים מעניקה לכם גישה חופשית לכל הקורסים שבאתר ובנוסף מאפשרת השתתפות בסדנאות לימוד Hands On. בסדנאות אלו ניפגש ב Remote לתכנת וללמוד יחד דרך התמודדות עם משימות פרקטיות ויישום בפועל של תכני הקורסים או נושאים אחרים שיעניינו אותנו. קראו עוד...