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

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

קוד כניסה (פייתון)

20/04/2018

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

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

import sys
class CodePanel:
    def __init__(self, code):
        self.code = code
        self.guess = Buffer(len(code))

    def check(self):
        if self.guess.get_as_string() == self.code:
            print('welcome')
            sys.exit(0)
        else:
            print('wrong code. try again')
            self.guess = Buffer(self.guess.size)

    def type_char(self, ch):
        self.guess.write(ch)
        if self.guess.is_full():
            self.check()

class Buffer:
    def __init__(self, size):
        self.buf = [''] * size
        self.at = 0
        self.size = size

    def write(self, ch):
        self.buf[self.at] = ch
        self.at += 1

    def is_full(self):
        return self.at == self.size

    def get_as_string(self):
        return ''.join(self.buf)


panel = CodePanel('13579')
code = '123413579'
for ch in code:
    panel.type_char(ch)

בכל פעם שמשתמש לוחץ על כפתור תיקרא הפונקציה CodePanel#type_char ותוסיף את האות לחוצץ עד שהחוצץ מתמלא, ורק אז נבדוק את הקוד ונדפיס הודעה. אפשר לראות שניסיתי להקליד את הרצף 123413579 ונשארתי בחוץ.

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

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

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

1
1, 2
1, 2, 3
1, 2, 3, 4
1, 2, 3, 4, 1
3, 2, 3, 4, 1
3, 5, 7, 9, 1

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

import sys

class CircularBuffer:
    def __init__(self, size):
        self.size = size
        self.buf  = [''] * size
        self.at   = 0

    def write(self, ch):
        self.buf[self.at] = ch
        self.at = (self.at + 1) % self.size

    def get_as_string(self):
        res = []
        for i in range(self.size):
            res.append(self.buf[(self.at + i) % self.size])
        return ''.join(res)

class CodePanel:
    def __init__(self, code):
        self.code = code
        self.guess = CircularBuffer(5)

    def check(self):
        if self.guess.get_as_string() == self.code:
            print(f'welcome: {self.guess.get_as_string()}')
            sys.exit(0)

    def type_char(self, ch):
        self.guess.write(ch)
        self.check()


panel = CodePanel('13579')
code = input()
for ch in code:
    panel.type_char(ch)

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

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

class CircularBuffer:
    def __init__(self, size):
        self.size = size
        self.buf  = deque([''], size)
        self.at   = 0

    def write(self, ch):
        self.buf.append(ch)

    def get_as_string(self):
        return ''.join(list(self.buf))

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

ציד באגים

19/04/2018

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

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

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

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

חרדיות בהייטק

18/04/2018

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

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

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

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

חרדת ביצוע

17/04/2018

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

הבעיה מתחילה כשחוויות כאלה מתחילות להשפיע עלינו לטווח הרחוק.

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

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

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

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

תיקונים

16/04/2018

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

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

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

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

ונעבור לחלק המקצועי

15/04/2018

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

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

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

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

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

משוררים ושרברבים

14/04/2018

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

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

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

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

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

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

tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q

כשהמציאות והתיעוד לא מסכימים

13/04/2018

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

use strict;

my %h = (
  foo => 10,
  bar => 20,
  buz => 30,
  has => 40,
  bil => 50,
  boo => 60,
);

print join(', ', values(%h)), "\n";

לא משנה כמה פעמים תריצו את הקוד הזה הוא תמיד ידפיס את המספרים 10,20,30,40,50,60 באותו הסדר. ועדיין התיעוד של פרל ציין במפורש שסדר המספרים שיודפסו במקרה כזה הוא מקרי אז אל תסתמכו עליו. אבל אנחנו הסתמכנו.

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

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

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

נ.ב. בנושא Push Notifications על אנדרואיד כדאי לקרוא כאן: https://medium.freecodecamp.org/why-your-push-notifications-never-see-the-light-of-day-3fa297520793

תרבות עבודה

12/04/2018

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

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

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

מסקנות?

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

תבניות מחשבה ושפות תכנות

11/04/2018

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

public class a {
  public static String checkSecurity(String [] people) {
    String found = "";
    int i=0;
    while (i < people.length && found.equals("")) {
      if (people[i].equals("Don")) {
        // report Don
        found = "Don";
      }
      if (people[i].equals("John")) {
        // report John
        found = "John";
      }
      i += 1;
    }
    return found;
  }

  public static void main(String [] args) {
    String res = checkSecurity(args);
    System.out.println(String.join(", ", args));
    if (res != "") {
      System.out.println("Found intruder: " + res);
    }
  }
}

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

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

interface StringPredicate {
  boolean check(String candidate);
}

public class b {
  public static String find_first(String [] people, StringPredicate p) {
    for (int i=0; i < people.length; i++) {
      if (p.check(people[i])) {
        return people[i];
      }
    }
    return null;
  }

  public static String checkSecurity(String [] people) {
    return find_first(people, (name) -> (name.equals("John") || name.equals("Don")));
  }

  public static void main(String [] args) {
    String res = checkSecurity(args);
    System.out.println(String.join(", ", args));
    if (res != "") {
      System.out.println("Found intruder: " + res);
    }
  }
}

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

def find_first(items)
  items.each do |item|
    break item if yield(item)
  end
end

found = find_first(ARGV) { |name| %w[John Don].include?(name) }
puts "Found Intruder: #{found}"

הקוד ברובי שקול (והרבה יותר קצר), אבל מה שחשוב שהוא גנרי. בעוד ש find_first של Java התעקשה לקבל פרדיקט של מחרוזות, ברובי קל להעביר פרדיקט כלשהו. לפני שאתם קופצים להמליץ לי על Generics שווה להזכיר שביטויי למדה לא עובדים עם פונקציות גנריות.

מתכנתי Ruby רגילים לתבנית של פונקציה שמקבלת Callback (נקרא ברובי בלוק) בגלל שהשפה מעודדת אותנו להשתמש במבנה זה. מתכנתי Java רגילים למבנים אחרים ומתכנתי JavaScript למבנים נוספים. וכאן אנחנו מגיעים לקושי במעבר בין שפות: לא מספיק לחפש איך לעשות את הדבר שאתם יודעים בשפה החדשה. יותר חשוב לחפש את התבניות שאתם לא רגילים לכתוב כי הן לא פופולריות בשפה הקודמת שלכם.