uz
Feedback
ToCode

ToCode

Kanalga Telegram’da o‘tish

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

Ko'proq ko'rsatish
1 421
Obunachilar
+224 soatlar
+17 kunlar
+130 kunlar
Postlar arxiv
ToCode
1 421
# השאלות הנכונות לא מזמן שיחקנו בחדר הבריחה הוירטואלי "הטנא האחרון". אחלה משחק ואני ממליץ לכם גם לקנות אם יש לכם ילדים בגיל המתאים. לקראת סוף המשחק אחרי שפותרים את כל החידות צריך לגלות קוד סודי שמהווה חלק מ URL. בזמן שכולם היו עסוקים בחיפוש הקוד הסודי אני כמובן לא יכולתי להתאפק והעדפתי למצוא את ה URL בעצמי עם קצת עזרה מגוגל. הסיבה שהצלחתי היא גם הסיבה לרוב בעיות אבטחת המידע שאנחנו נתקלים בהן כשאנחנו כותבים קוד: אנחנו שוכחים או לא יודעים לשאול את השאלות הנכונות. כי רק אם אני יודע שיהיה איזה פסיכי שיחפש את הקישור לא בדרך הרגילה, אני יכול להמשיך ולשאול "איך גורמים לקישור מסוים להישאר סודי". בדיוק כמו שרק אם אני יודע שיהיה איזה פסיכי שיפרסם קוד JavaScript בתוך גוף אימייל אני יכול להמשיך ולשאול "איך אני מבצע Escaping נכון לתוכן גולשים". והדרך להגיע למצב שאנחנו יודעים את השאלות הנכונות מתחילה בלשאול את השאלות הלא נכונות, להסתכל על התשובות ועל שאלות שאנשים אחרים שואלים ולאט לאט ללמוד. לא יקרה ביום אבל התוצאה שווה את המאמץ.

ToCode
1 421
# כמה בעיות עם Feature Branches ומה אפשר לעשות במקום שיטת העבודה המפורסמת ביותר עם גיט נקראת Git Flow. השם הקליט מסתיר שמדובר בבחירת שיטת עבודה שלא מתאימה לכל מי שעובד בגיט, שיש אופציות אחרות ושיש בה בעיות מהותיות שאולי לא מתאימות לפרויקט שלכם. העיקרון הבעייתי מ Git Flow עליו אני רוצה להתמקד היום הוא השימוש ב Feature Branch. קודם כל אלה עיקרי המנגנון מתוך המדריך Gitflow Workflow: 1. רוב העבודה מתבצעת על ענף בשם Develop. 2. כל פיצ'ר חדש ייכתב ב Branch משלו. 3. כשהפיצ'ר מוכן נשלב אותו חזרה ל Develop. 4. מתישהו כשיש מספיק פיצ'רים או כשיעבור מספיק זמן נוציא מ Develop ענף חדש בשם Release ושם נעבוד על ייצוב הפיצ'רים ואינטגרציה. בעבודה על פרויקטים בגישה כזאת נתקלתי ב-4 הבעיות הבאות: 1. אינטגרציה ארוכה ורווית קונפליקטים - כי כשיש לי שני מתכנתים שעובדים על פיצ'רים שמתנגשים, הקונפליקט בין הפיצ'רים לא יעלם אם אני אחכה שהפיצ'רים יהיו מוכנים. בדרך כלל הקונפליקטים רק יחמירו ככל שאותם מתכנתים כותבים יותר קוד, ואז כשצריכים למזג אנחנו צריכים לעדכן קוד שכתבנו לפני שבועיים-שלושה, שלא תמיד זוכרים אותו כמו שצריך. 2. פיצ'רים שאף פעם אין זמן לשלב אותם- כי אם אנחנו ממזגים פיצ'ר רק כשהוא מוכן אז מישהו צריך לבדוק שהפיצ'ר אכן מוכן. כאן יכול להיווצר צוואר בקבוק על ראש הצוות או ה QA ובעצם יהיו לנו פיצ'רים שיחכו הרבה זמן עד שהארגון יהיה מוכן לשלב אותם. 3. קשה "להתחרט" כשמגלים שפיצ'ר מסוים לא מתאים לגירסה- וזה מתחבר ל (2) כי הסיבה שאנחנו לא משלבים פיצ'ר ש"נראה מוכן אבל עדיין לא לגמרי" זה שאם הגעתי לעבוד על ה Release Branch ואני מגלה שהפיצ'ר המסוים הזה לא מספיק מוכן ורק מעכב לי את הגירסה, אין לי דרך קלה להתחרט ולהוציא אותו מ Develop או מ Release. 4. בזבוז זמן כתוצאה ממיזוג פיצ'ר מאוחר מדי- הרבה פעמים יהיה לי פיצ'ר תשתיתי שמשנה את איך שעובדים עם המערכת מכאן והלאה. לדוגמה פיצ'ר של "תמיכה בריבוי שפות" שמעביר את כל הטקסטים במערכת לקבצי הגדרות חיצוניים. ככל שאני מחכה עם שילוב הפיצ'ר הזה כך מתכנתים אחרים כותבים בפיצ'רים שלהם טקסטים חדשים בשיטה הישנה, ואז כשאני אגיע לשלב את הפיצ'ר התשתיתי אצטרך יותר עבודה לתקן גם את כל הפיצ'רים האחרים שנכתבו עם התשתית הישנה. חלופה מעניינת ל Git Flow נקראת Continious Integration. מארטין פולר כתב כהרגלו מאמר ארוך על הנושא עוד ב 2006 והוא רלוונטי גם היום. הרעיון בגדול הוא שכולם עובדים על אותו Branch ומשתמשים ב Feature Flags כדי שפיצ'רים מסוימים לא יוצגו ב UI. כמו בהרבה מקרים בפיתוח אין פיתרון אחד שמתאים לכולם ולכל הצוותים בעולם, אבל אם גם אתם נתקלתם בבעיות בעבודה עם Branch-ים שווה להכיר ש Git Flow הוא לא הדרך היחידה לעבוד ואולי תגלו שבשיטה אחרת אתם מצליחים להתקדם מהר יותר.

ToCode
1 421
# חדש ב Node 16 - ממשק Promises ל Timers נוד 16 יצא לאחרונה ואיתו המון פיצ'רים חדשים ולא מאוד מעניינים (כאן יש רשימה גדולה). פיצ'ר אחד שתפס לי את העין ושווה לשתף הוא ההכנסה של ממשק Promises למודול השעונים. זה נראה כך:
import { setTimeout } from 'timers/promises';

async function doSomething() {
  console.log('doSomething started!');
  await setTimeout(2000);
  console.log('Delayed Timers!');
}

doSomething();
נכון, אנחנו יודעים לכתוב setTimeout שמחזיר Promise לבד, ובכל זאת נחמד לראות דברים כאלה נכנסים לשפה והופכים לסטנדרט. המעבר ל API מבוסס Promises מלווה אותנו תקופה ארוכה ב Node ובדפדפן. באותו הקשר ומאותה רשימה, נוד 16 קיבל גם תמיכה ב Abort Controller שזה פיצ'ר שהרבה אנשים חיכו לו ומאפשר ביטול בקשת fetch. אפשר לקרוא עליו ב MDN יחד עם דוגמה לשימוש בפיצ'ר בדפדפן בקישור כאן: https://developer.mozilla.org/en-US/docs/Web/API/AbortController. אני לא בטוח כמה הוא יעזור ב Node.JS אבל בכל מקרה טוב שיש לטובת מי שכותבים קוד אוניברסלי. יש כבר אימג' בדוקר האב אז הפקודה הבאה מכל מכונה תזרוק אתכם לתוך REPL של נוד 16 כדי לנסות את השטויות החדשות:
$ docker run -it --rm node:16 node

ToCode
1 421
# ברגע שזה אפשרי בן אדם עובד, שם קצת כסף בצד כל חודש ואחרי שנה או חמש החיסכון גדל ויום אחד אתה שם לב שיש לך מספיק כסף בחשבון לאוטו החדש שתמיד רצית. או בן אדם יושב לכתוב מערכת, יום אחרי יום, שבוע אחרי שבוע, ובהתחלה כמו כל מערכת זה לא ממש יציב והממשק משתנה כל יומיים ובאמת אין דרך לכתוב בדיקות לדבר הזה כי הכל נשבר ונבנה מחדש, אבל אז אתה קולט שכבר שבועיים המבנה נשאר די קבוע ובסך הכל אם תתחיל עכשיו לכתוב בדיקות יש כבר כמה דברים שאפשר לבדוק וכנראה יישארו יציבים. או שעבדת כבר שלוש שנים באותה משרה וצברת ניסיון רלוונטי ומדי פעם מתחילות להגיע הצעות מכל מיני מגייסים בלינקדאין, לא משהו וודאי אבל בכל זאת משהו שאפשר להתחיל לעבוד איתו וכשאת מבררת אצל חברים את מגלה שיש לא מעט תפקידים טובים, בתנאים יותר טובים ובשכר יותר טוב שאת יכולה לקחת. לכל שינוי יש את הרגע שבו פתאום זה אפשרי, פתאום אפשר להתקדם ולעשות את השינוי. וגם אם אף פעם לא נצליח לראות בזמן אמת את השניה המדויקת בה זה קרה ככל שיעבור זמן כבר יהיה ברור שהשינוי אפשרי. שמשהו שעד עכשיו היה חלום עכשיו יכול להפוך למציאות. ואז הכל מתחיל להישבר והמחשבות תוקפות: 1. בשביל מה לקנות עכשיו אוטו חדש, הסובארו הישנה נוסעת ממש בסדר. 2. ולמי בכלל יש זמן לזה? 3. ויש לי טסט לעוד שלושה חודשים חבל לא לנצל אותם. 4. נו ובטח באותו יום שאקנה את האוטו מישהו יקנא וישרוט לי אותו. 5. וממילא למכוניות יש ירידת ערך מטורפת עדיף לחכות עוד שנה. אתם יכולים לנסות את המשחק הזה עם כל אחד משלושת הסיפורים ותראו למה אנחנו נתקעים בעבודות שכבר קטנות עלינו, נתקעים עם טכנולוגיות ישנות ואולי ממשיכים לעשות טסט פעמיים בשנה. שווה לשים לב שלבחירה להישאר במקום יש מחיר: כל יום שאת לא משלבת בדיקות אוטומטיות בקוד את רק כותבת עוד יותר קוד בלי בדיקות, וזה הולך להיות עוד יותר קשה להוסיף אותן; כל יום שאת נשארת בעבודה הישנה את רק מתרחקת מהטכנולוגיות החדשות בשוק ומפסידה הזדמנויות לתפקידים טובים יותר; וכל שנה נוספת עם הסובארו את רק משלמת כפול על תיקונים וטסטים. הדבר המפחיד הוא לא להתקדם ולעשות את השינוי זה ההיפך - הדבר המפחיד הוא שהאפשרות לשינוי תתרחק ותהפוך ללא רלוונטית. ברגע שזה אפשרי זה הזמן הכי טוב להתחיל.

ToCode
1 421
# שאלות לראיונות עבודה: הפקודה throw ב Node.JS במדריך Hello World של RabbitMQ הם הציגו את הקוד הבא בתור דוגמה לקוד ששולח הודעה לתור הודעות:
var amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', function(error0, connection) {
  if (error0) {
    throw error0;
  }
  connection.createChannel(function(error1, channel) {
    if (error1) {
      throw error1;
    }
    var queue = 'hello';
    var msg = 'Hello world';

    channel.assertQueue(queue, {
      durable: false
    });

    channel.sendToQueue(queue, Buffer.from(msg));
    console.log(" [x] Sent %s", msg);
  });
});

דני דפדפני רצה לשים את הקוד בתוך פונקציה ולהפעיל אותה בתוך try/catch כדי להציג הודעת שגיאה יפה יותר אם החיבור לתור נכשל, אז הוא עדכן את הקוד שיראה כך:
var amqp = require('amqplib/callback_api');

function doSend() {

    amqp.connect('amqp://localhost', function(error0, connection) {
    if (error0) {
        throw error0;
    }
    connection.createChannel(function(error1, channel) {
        if (error1) {
        throw error1;
        }
        var queue = 'hello';
        var msg = 'Hello world';

        channel.assertQueue(queue, {
        durable: false
        });

        channel.sendToQueue(queue, Buffer.from(msg));
        console.log(" [x] Sent %s", msg);
    });
    });
}

try {
    doSend();
} catch (err) {
    console.log('Sorry connection failed. Try again next year');
}
ולשאלות: 1. האם התוכנית של דני עבדה? האם תודפס הודעת השגיאה שלו במקרה של כישלון בחיבור? 2. למה היא לא עבדה? 3. איך הייתם מעדכנים את הקוד, בלי לשנות את קוד הפונקציה doSend או החתימה שלה, כדי להציג את הודעת השגיאה של דני? 4. איך הייתם ממליצים לעדכן את קוד הפונקציה doSend והחתימה שלה כדי שיהיה יותר קל לקוד חיצוני לטפל בשגיאות? אפשר לפרסם את התשובות שלכם בתגובות או אם אתם לא בטוחים שווה לקרוא את המדריך הזה כדי ללמוד איך לטפל בשגיאות ב Node.JS.

ToCode
1 421
# טיפ דוקר: שימוש ב Access Tokens במקום בסיסמאות אם אתם כותבים אימג'ים ומעלים אותם ל Docker Hub וודאי יש לכם כבר חשבון ב Docker Hub וכבר חיברתם את כלי שורת הפקודה לחשבון עם docker login. מה שלא בטוח ששמתם לב זה ש docker login יצר קובץ בשם config.json בתוך תיקיית .docker בתיקיית הבית שלכם, ובתוכו דוקר כתב ב Plain Text את הסיסמה שלכם לדוקר האב. עכשיו אני מבין דוקר, צריך לשמור את הסיסמה איפשהו, וקובץ לא מוצפן על המחשב נראה למישהו בטח כמו רעיון טוב. אנחנו לא פה בשביל לשנות את העולם. מה שכן אפשרי ושווה את המאמץ זה להשתמש במשהו שנקרא Personal Access Token במקום סיסמה לדוקר האב. השימוש ב Personal Access Token מאובטח (קצת) יותר מסיסמה בגלל ש: 1. אפשר לראות דרך ממשק הניהול שלהם איזה פעולות נעשו דרך ה Access Token, ואם היה משהו חשוד אז לבטל. 2. אפשר להשתמש ב Access Token שונה לכל מכונה, ואז לפחות תדעו איזה מכונה נפרצה (כי אי אפשר עדיין להגדיר הרשאות ל Access Token). 3. בחיבור דרך Access Token אי אפשר לעשות פעולות ניהוליות בחשבון כמו החלפת סיסמה. יצירת ה Access Token היא ממש פשוטה - נכנסים לממשק הניהול של דוקר האב, בוחרים בטאב Security ולוחצים New Access Token. בשביל להתחבר עם ה Access Token מפעילים docker login וכשדוקר מבקש את הסיסמה אנחנו מדביקים את ה Access Token במקום. והתוצאה: יש לנו קובץ config.json בתיקיית ~/.docker שמכיל את אותו Access Token במקום את הסיסמה. שמרו עליו טוב.

ToCode
1 421
# מבלבל בכוונה מחוץ לקונטקסט, כשאנחנו לא בעניינים, הרבה פיצ'רים מרגישים מבלבלים בכוונה. למה Dockerfile צריך את הפקודה EXPOSE? או VOLUME? למה ריאקט היה צריך את componentWillMount (שלשמחתנו כבר בוטל)? למה typeof null צריך להיות object ב JavaScript? ולמה HTML Audio צריך לקבל ילדים מכל מיני פורמטים, כשיש פורמט אחד שכל הדפדפנים יודעים לנגן? היו רגועים. אף אחד לא ניסה לבלבל אתכם. רוב הפיצ'רים שמרגישים כאילו הם שם רק בשביל לבלבל הם פשוט תוצאה של התגלגלות אירועים היסטורית, או רעיונות שנראו פעם טובים והיום כבר נשמעים פחות טובים. טכנולוגיה לא נכתבת כמקשה אחת בבום אלא מתפתחת בתהליך איטי של אבולוציה וכתוצאה של דיאלוג וריבים בין שחקנים מתחרים. כשאתם יושבים ללמוד טכנולוגיה חדשה כמעט תמיד שווה להשקיע את הזמן גם בכניסה לאותו סיפור היסטורי של הטכנולוגיה. איך כתבו קומפוננטות בריאקט בגירסה הראשונה שלה? איזה בעיות היו להם? מה דחף אותם לשינוי הממשק? ואיזה בעיות חדשות היו בממשק החדש, שהביאו לשינוי ממשק שלישי? מי האנשים שהחליטו על שינויים אלה? ומה היה חשוב להם כשהם עיצבו, איפיינו ומימשו את אותם שינויים? רק ככה אפשר להבין את כל הפינות של השפה, כולל אלה שנראות לגמרי נטולות הגיון.

ToCode
1 421
# גם המוח שלכם עושה את זה? המוח האנושי הוא איבר מופלא. הוא יודע להתאים את עצמו למצבים משתנים ותמיד למצוא פיתרונות לבעיות. אבל לפעמים המוח האנושי כל כך רוצה להציל אותנו מבעיות שהוא ממציא בעיות איפה שאינן קיימות והחילוץ רק עושה יותר נזק. קחו למשל מצב שאתם עובדים על בעיית תכנות יום שלם ובסוף היום לא הגעתם לשום תוצאה. במקום להסתכל על היום שהיה ולשמוח על הזמן שהשקעת בללעוס את הבעיה, המוח האנושי מסתכל אחורה וחושב "אני לא מספיק טוב", "לעולם לא אפתור את זה" ו"חייבים למצוא דרך מילוט כי אין מצב שאני פותר את האתגר". ואז הכיף מתחיל. במקום לעבוד בלפתור את הבעיה המוח האנושי רק מחפש לשכנע אותך לעקוף אותה: 1. אולי רק אבדוק כמה דקות מה חדש ב ynet? (ביי ביי 10 דקות יקרות) 2. אולי יש ספריה שמישהו כבר כתב שפותרת את זה? (ביי ביי 5 שעות שלעולם לא יחזרו) 3. אולי אפשר לשכנע את הבוס שבעצם המשימה הזאת לא חשובה? (ביי ביי שעה וחצי שיחת טלפון ועוד שעה להירגע מהשיחה) 4. אולי אני בתחום הלא נכון? (ביי ביי חצי שעה על מחשבות חסרות ערך) 5. בחברה הלא נכונה? 6. בחיים הלא נכונים? 7. מה קורה עם ההתחממות הגלובאלית עכשיו? 8. רגע כבר זמן לאכול צהריים? ערב? לסגור את המחשב? אם גם המוח שלכם עושה את זה שווה להזכיר לו שמשימות לא פותרים בחצי שעה או ביום; שהרבה פעמים יש התקדמות גם אם קשה למדוד אותה ושהדרך היחידה לעבור את זה היא קדימה. מחר מנסים שוב.

ToCode
1 421
# סטאקבליץ, נוד ו Web Assembly סטאקבליץ הודיעו לאחרונה שהצליחו לקמפל את Node.JS בתוך Web Assembly ועל הדרך חיברו את התקני הרשת שלו ל Service Worker API. הנה מה שזה אומר בשבילנו ובקצרה: 1. סטאק בליץ בונים סביבת פיתוח בתוך הדפדפן, קצת כמו VS Code רק אונליין. 2. ווב אסמבלי הוא מנגנון של דפדפנים שמאפשר לקחת תוכניות מכל שפת תכנות ו"לקמפל" אותן ל JavaScript כך שירוצו מהר ובצורה טבעית בתוך הדפדפן. ווב אסמבלי הולך לשנות את העולם בהמון דרכים שאנחנו עכשיו רק מתחילים לראות אותן. 3. קימפול נוד בתוך דפדפן אומר שדפדפן כרום שלכם מריץ Node.JS. לא שרת מרוחק שמריץ ואנחנו מתחברים אליו אלא ממש הדפדפן עצמו. התוצאה היא תוכניות שרצות מאוד מהר כי הכל קורה מקומית. 4. אבל זה לא נגמר שם - כי כשאתה מחבר את קווי הרשת של Node.JS ל ServiceWorker API אתה בעצם משתלט על התקשורת היוצאת מהדפדפן: כל בקשת רשת שיוצאת מהדפדפן עוברת קודם דרך אותו Node.JS פנימי שרץ אצלי בתוך הדפדפן. זה אומר שדפדפן יכול "לפנות" לשרת שרץ גם הוא בתוך הדפדפן, ובאופן אוטומטי ה Service Worker יתפוס את הבקשה לפני שהיא יוצאת מהדפדפן ויעביר אותה לאותו תהליך Node.JS פנימי שרץ באותו דפדפן עצמו. 5. התוצאה היא סביבת פיתוח מלאה בענן שבעצם רצה מקומית בתוך הדפדפן שלכם (כולל צד השרת), ועובדת גם בלי אינטרנט. 6. וכאילו שזה לא מספיק, בזכות השימוש ב PWA בתוך סטאקבליץ יש כפתור "Install" שמאפשר להתקין אותם בתור אפליקציית Desktop. התוצאה באמת מרשימה. אני יצרתי בשתי לחיצות שרת Next.JS שמריץ ריאקט וגם קוד השרת וגם קוד הלקוח רצו אצלי בתוך הדפדפן. יש להם אינטגרציה מופלאה עם Chrome Devtools כך שאפשר מתוך ה Debugger לראות את כל קוד צד השרת ולשים נקודת עצירה בכל מקום, בין אם זה בקלאיינט או בסרבר. הקישור הזה (לפתוח מכרום בלבד) ייצור גם לכם כזה שרת בדפדפן: https://stackblitz.com/fork/nextjs שווה לנסות. העתיד של הרשת מעולם לא נראה מעניין יותר.

ToCode
1 421
# פיתרון Advent Of Code 2015 יום 3 ב Ruby בהתקף נוסטלגיה החלטתי להסתכל על קצת חידות ישנות של Advent Of Code ונתקלתי בחידת היום השלישי של 2015 שפשוט צעקה רובי וגם לימדה אותי משהו על שימוש חוזר בקוד. ## החידה - חלק 1 סנטה צריך לחלק מתנות לילדים ולכן עובר בין הבתים לחלק את המתנות. הבתים מסודרים על רשת דו-מימדית והאלפים נתנו לסנטה הוראות ניווט עם חצים: חץ שמאלה ללכת מערבה, חץ ימינה ללכת מזרחה, חץ למעלה ללכת צפונה וחץ למטה ללכת דרומה. הבעיה שמי שכתב את ההוראות לא שם לב שחלק מהמסלול עובר כבר בבתים שסנטה כבר ביקר בהם ולכן לא צריך להשאיר שם מתנה פעם שניה. לדוגמה עבור דף הוראות שנראה כך:
^>v<
סנטה ילך צעד אחד צפונה, אחר כך צעד אחד מזרחה, אחר כך צעד אחד דרומה ואז צעד אחרון מערבה, וסך הכל יבקר ב-3 בתים שונים (בבית הראשון הוא יבקר פעמיים). בהינתן דף הוראות עיזרו לסנטה לגלות בכמה בתים שונים הוא הולך לבקר. ## הפיתרון תנועה על רשת דו-מימדית? אני יודע את זה! כל נקודה בגריד היא בעצם מספר מרוכב. מתחילים במספר 0+0i, כל אחד מארבעת החצים מקבל מספר מרוכב שמתאים לכיוון שלו וכל צעד הוא בסך הכל פעולת חיבור. בקוד רובי זה נראה כך:
MOVEMENT = {
  ">" => 1,
  "<" => -1,
  "^" => 1i,
  "v" => -1i
}.freeze

def all_houses(path)
  path
    .chars
    .map { |c| MOVEMENT[c] }
    .inject([0 + 0i]) { |acc, val| acc << acc[-1] + val }
end

def unique_houses(path)
  all_houses(path)
    .uniq
    .size
end

# part 1
input = File.read("input.txt").strip
puts unique_houses(input)
הפונקציה all_houses מחזירה מערך של כל הבתים שסנטה ביקר בהם, והפונקציה unique_houses לוקחת מערך כזה ומחזירה רק את מספר הערכים הייחודיים ממנו. ## חלק 2 בחלק השני של החידה סנטה מקבל רובוט עוזר. הם מחלקים את דף ההוראות כך שסנטה מקבל את ההוראות במקומות האי-זוגיים והעוזר את ההוראות במקומות הזוגיים. עכשיו אותו דף הוראות ^>v< יגרום לסנטה ולעוזר שלו לבקר בסך הכל ב-3 בתים: סנטה ילך בית אחד צפונה ואחד דרומה, והעוזר ילך בית אחד מזרחה ואז צעד אחד חזרה מערבה. יחד עם העוזר - בכמה בתים שונים ביקרו סנטה והרובוט? ## פיתרון חלק 2 בהינתן כל הקוד שכתבנו עד עכשיו אפשר לפתור את החלק השני ממש בקלות - נפצל את דף ההוראות לזוגיים ולאי-זוגיים ומשתמשים בפונקציה פעמיים. ברובי אפשר לפצל מערך למקומות הזוגיים והאי-זוגיים באופן הבא:
santa, robot = input.chars.partition.each_with_index {|_, i| i.even? }
הבעיה שעכשיו במשתנים santa ו robot יש לנו מערכים במקום מחרוזות, ולכן אי אפשר לשלוח אותם לפונקציה all_houses שיצרנו קודם. בנקודה הזאת הדבר הנכון לעשות פשוט לא הסתדר עם השכל הישר. הדבר הנכון לעשות היה לפצל את הפונקציה all_houses לפונקציית עזר נוספת שמקבלת את המערך ולקרוא לה מתוך הפונקציה המקורית. בקוד החדש להפעיל ישירות את הפונקציה שמקבלת את המערך והכל בסדר. הבעיה שלי עם גישה כזאת היא שהיא "מגדילה" את הממשק, כלומר במקום שתהיה רק דרך אחת להיכנס למסלול חישוב הבתים עכשיו יש לנו שתי נקודות כניסה, וגם לתחזוקה של זה יש עלות. לכן הפיתרון שלי היה ללכת נגד הביצועים ולתת למחשב לעבוד קצת יותר קשה, מתוך הבנה של הקונטקסט של התרגיל. במקום לארגן מחדש את הפונקציות פשוט חיברתי את המערכים חזרה למחרוזת וקיבלתי את הקוד הבא שפותר את השאלה:
# part 2
santa, robot = input.chars.partition.each_with_index {|_, i| i.even? }.map(&:join)
puts (all_houses(santa) + all_houses(robot)).uniq.size
לא הכי יעיל, וכולל שכפול קוד של מנגנון ה unique_houses, אבל כנראה שיותר קצר מתוספת פונקציות.