מסיבת חילופי זוגות

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

אז הנה בעיה בסיסית מאד. איך מחליפים ערכים מספריים בין שני משתנים ב-JS?

התשובה הכי טריוויאלית היא זו:

let a = 1
let b = 2
let c = a
a = b
b = c

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

העלילה מסתבכת

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

נעשה את זה מסודר:

let a = 1
let b = 2
a = a + b // a = 1 + 2 === 3
b = a - b // b = 3 - 2 === 1
a = a - b // a = 3 - 1 === 2

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

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

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

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

let a = 'a'
let b = 'b'
a = a + b // a = 'a' + 'b' === 'ab'
b = a[0] // b = 'a'
a = a[1] // a = 'b'

העלילה מסתבכת עוד יותר

יכולנו לסיים כאן, אבל אנחנו יצירתיים. בואו נסבך.

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

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

let a = 'abc'
let b = 'defg'
a = a + b // a = 'abe' + 'defg' === 'abedefg'
b = a.substr(0, a.length - b.length) // b = substr(0, 7 - 4) === 'abc'
a = a.substr(b.length) // a = substr(3) === 'defg'

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

הגענו לשיא? אפשר לפרוש? לא ולא! עכשיו נגיע לשוס האמיתי.

השוס האמיתי

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

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

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

let a = 123
let b = 'hello world'
a = [a, b] // a = [123, 'hello world']
b = a[0] // b = 123
a = a[1] // a = 'hello world'

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

לסיכום

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

ועכשיו בונוס קטן.

בונוס קטן

אפשר גם ככה.

let a = 'abc'
let b = 'defg'
[a,b]=[b,a]

לפוסט הזה יש 4 תגובות

  1. Maroun

    כיף לקרוא! כתוב בסגנון מיוחד ובצורה מושכת!

    1. גיל

      תודה רבה. כיף לשמוע!

  2. שלומי

    שאלה סופר קלאסית עם התאמה לJS עם דוגמא מES6 אהבתי! תודה

    1. גיל

      תודה רבה, שלומי!

התגובות נעולות.