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

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

טכניקה פשוטה לתוויות צפות ב CSS

27/04/2020

אחד הטריקים השימושיים באתרי מובייל הוא להפוך את ה Placeholder של תיבת קלט ל Label של אותה תיבה. במילים אחרות במקום לכתוב תווית Email ואחריה תיבת טקסט עבור אימייל עם placeholder שמייצג קלט אפשרי כמו זה:

<label>
    Email:
    <input type="email" placeholder="demo@gmail.com" />
</label>

אנחנו רואים תיבות טקסט שנראות כך:

<input type="email" placeholder="Email Address" />

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

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

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

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

קוד? ברור. הנה ה HTML:

<label>
  <input type="text" placeholder="name" />
  <span class="label-text">Name</span>
</label>

וזה ה CSS:

label {
    position: relative;
    height: 50px;
    display: flex;
    flex-direction: column-reverse;
}

.label-text {
  left:-10000px;
  position:absolute;
  top:auto;
  width:1px;
  height:1px;
  overflow: hidden;
  background: orange;
  color: white;
}

label input {
  font-size: 24px;
  height: 100%;
  border: 1px solid orange;
  box-sizing: border-box;
}

input:focus::placeholder {
  color: transparent;
}

input:focus + .label-text {
  position: relative;
  width: auto;
  height: auto;
  left: 0;
}

input:focus {
  font-size: 18px;
  height: auto;
}

שימו לב שבגלל מגבלות של CSS אני צריך לכתוב את ה label-text אחרי ה input (כדי שאוכל להשתמש בסימן הפלוס לעצב את ה label-text).

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

ויש גם קודפן בקישור: https://codepen.io/ynonp/pen/Vwvbrxm, או בהטמעה:

זיהוי פנים ב Python עם OpenCV

25/04/2020

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

המשך קריאה

בשעה טובה, גם לנו יש Server Side Rendering (שוב)

24/04/2020

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

המשך קריאה

טיפ CSS: המילה matches

23/04/2020

אחת היכולות המדליקות של Scss שהרבה זמן היתה חסרה לי ב CSS היא היכולת לארגן Selectors אחד בתוך השני. כלומר במקום לכתוב:

header h1,
header h2,
header h3,
header h4,
header h5,
header h6 {
  font-weight: normal;
}

אנחנו יכולים לכתוב פשוט:

header {
  h1, h2, h3, h4, h5, h6 { font-weight: normal; }
}

מסתבר שממש בקרוב נוכל להתחיל להשתמש בכתיב שנקרא matches כדי להגיע לאותה תוצאה בדיוק ב CSS:

header :matches(h1, h2) {
  font-weight: normal;
}

הבעיה היחידה אגב שבינתיים דפדפנים לא ממש תומכים בכתיב זה. בצד החיובי רובם כן תומכים בדיוק בפיצ'ר הזה בשם אחר - באמצעות מילת הקוד any ותוספת Vendor Prefix. כלומר הקוד הבא כן עובד ברוב הדפדפנים המודרניים היום:

header :-moz-any(h1, h2) {
  font-weight: normal;
}

header :-webkit-any(h1, h2) {
  font-weight: normal;
}

כך שאם אתם מריצים סקריפט שמוסיף Vendor Prefix בצורה אוטומטית או ב prefixfree אתם כבר יכולים להנות מהכתיב החדש (ורק תזכרו שבקרוב הכתיב של any יהפוך ל matches ותהיו מוכנים להחליף כשיגיע היום).

חידת ראיונות עבודה בשפת Java

22/04/2020

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

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

 public static boolean isPelindrome(int number) {
    String reverseNumberString = new StringBuilder(number).reverse().toString();
    String numberString = String.valueOf(number);
    return reverseNumberString == numberString;
  }

מצאתם את הבעיות? ספרו בתגובות מה זה היה.

איך מגבילים מספר פעולות בדקה

21/04/2020

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

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

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

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

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

  2. בכל תא במערך רושמים את השעה הישנה ביותר שמחשב תומך בה (מיוצגת על ידי המספר 0 - מספר השניות מאז ה 1/1/1970).

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

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

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

הפונקציה המרכזית ברובי מהקוד היא:

    def shift
      time = nil

      @mutex.synchronize do
        time = @ring[@head]

        sleep_until(time + @interval)

        @ring[@head] = Time.now
        @head = (@head + 1) % @size
      end

      time
    end

ואת שאר המחלקה תוכלו למצוא בקוד של הג'ם בגיטהאב Limiter Gem.

עם האוכל בא התיאבון

20/04/2020

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

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

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

למה זה דומה

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, למרות שבפייתון המימוש הרבה יותר אלגנטי. ככל שאנחנו לומדים יותר לעומק איך השפה עובדת אנחנו יכולים למצוא נקודות דימיון אמיתיות לשפות וטכנולוגיות אחרות, ובאמת להסיק ממקום אחד לגבי מקומות אחרים - אבל צריך תמיד לזכור שיש גם צד מבלבל לסיפור, ושלפני שאנחנו מכירים טוב שפה יהיו לנו הרבה מצבים שמשהו "מצלצל מוכר" ומזכיר משהו משפה אחרת, אבל זה רק נראה כמו דומה.

קפיצה מטאורית

18/04/2020

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

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

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

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