שלום לכם!
פרסמתי פוסט בנושא בבלוג code-review.blog, אך בעידוד בעל הבית, ר' חיים דיקמן הי"ו, אפרסם גם כאן:
בקורס תכנות פונקציונאלי, קבלנו מטלה עבורה הייתי צריך ליצור מכפלה קרטזית של מערך עם עצמו, כלומר הקלט הוא מערך נניח
(1,2,3,4)
והפלט צריך להיות:
((1,1),(1,2),(1,3),(1,4)...(n,n))
התרגיל היה כפוף למגבלה: אסור להשתמש בלולאות או ליצור "אפקט צד"…
התרגיל היה בScala אך בעקבות דיון בקבוצת הוואטסאפ DDOS הגעתי להצעות בכמה שפות,
אשתף:
קוד סקלה ליצירת מכפלה קרטזית
הקוד כולו פונקציונאלי: המערך עובר מיפוי, עבור כל איבר מתקבל מערך המכיל זוגות זוגות של אברים, שהם עצמם נוצרו כתוצאה מZIP על זוג מערכים, הראשון הוא המערך המקורי עצמו, כולו, והשני הוא מערך המכיל את האיבר הראשון עצמו, משוכפל שוב ושוב עד אורך המערך המקורי, וכעת כל המערכים האלו עוברים שיטוח למערך יחיד.
הקוד נראה לי די קריא ותמציתי, אך כיוון שלמדתי לכתוב בScala בערך יומיים קודם, ייתכן שאפשר לקצרו יותר או לעשותו בהיר יותר.
הקוד בפייתון ללא ספק ארוך יותר וקריא פחות, ראשית יוצרים מערך של מערכים, המכיל את המערך המקורי משוכפל שוב ושוב, ואב יוצרים מערך של מערכים המכיל את המערך המקורי כולו, משוכפל שוב ושוב, ואז משדכים אותך עם ZIP ואז עם MAP משדכים כל איבר לזוג זוג של אברים… ולבסוף עם SUM משטחים הכל למערך חדש.
כמה דברים מפריעים לי בקוד הזה, ראשית הוא ארוך ולא ממש אינטואיטיבי, שנית, בשורה 5 אנו נאלצים להמיר את הZIP לליסט, ובכך "להפעיל" את הZIP על כל האברים.
בלי זה נקבל שגיאה. אך זה מונע ייצור "עצל" של הזוגות ויהווה בעיה עבור מערך ארוך מאד.
ואחרון חביב, אני בעצם לא רוצה Set אלא List אבל נאלץ להשתמש בו כדי למנוע כפילות… אם בהמשך ארצה להשתמש בו כמערך, אצטרך להמיר לList. אולי מראש כדאי אפילו לעשות כן בעת ההחזרה מהפונקציה, ומ"מ זה מוסיף תקורה.
הקוד באדיבות אלעד הלר
הקוד בJS מאד קצר ויפה, המערך עובר מיפוי מיוחד הכולל שיטוח, ומה שמשוטח הוא תוצאות Map עם Map פנימי, בכעין לולאה בתוך לולאה, היוצרת זוגות זוגות של המערך המקורי עם עצמו.
מצד שני קשה לומר שזה תחרות אמיתית, המגבלה של העדר לולאות הכפויה, מונעת את ניצול היכולות האמיתות הן של Python הן של Scala…
לשם המחשה, עם לולאות בScala ניתן לכתוב ככה:
אבל במסגרת תחרות זו "עם ידיים קשורות מאחורי הגב" לדעתי JS ניצחה…
הערה: אני לצערי לא בקיא מאד בפייתון, למרות שאני אוהב מאד את השפה…
(המכללה בישלה דייסה, נתנה סימסטר אחד לC, סימסטר אחד לC++ ושני סימסטרים לJava (איכס…) ולפייתון לא נשאר… הלכה פייתון לחפש דייסה… ולא נשאר מאומה, הזמן שלי כבר נתפס כולו… )
כך שאולי לחינם דנתי את פייתון לחובה… אם למישהו יש פתרון יעיל וקריא יותר, אשמח לעמוד על טעותי.
תוספת על פי תגובות ידידיי הי"ו:
החכימני ידידי ר' דוד כץ, שיש לכך בפייתון פונקציה מובנית בספריה Itertools:
https://docs.python.org/3/library/itertools.html#itertools.product
אם נביא אותה בחשבון, פייתון כמובן מנצחת… אך מצד שני, "מה החכמה" כאן, הרי אחרי שכתבתי פונקציה, תמיד לקרוא לה זה מילה אחת… וכאן מאחורי הקלעים יש קוד הכולל לולאות… ובפועל הוא אפילו ארוך יותר ובC.
המקור כאן
פרסמתי פוסט בנושא בבלוג code-review.blog, אך בעידוד בעל הבית, ר' חיים דיקמן הי"ו, אפרסם גם כאן:
על מכפלה קרטזית
בקורס תכנות פונקציונאלי, קבלנו מטלה עבורה הייתי צריך ליצור מכפלה קרטזית של מערך עם עצמו, כלומר הקלט הוא מערך נניח
(1,2,3,4)
והפלט צריך להיות:
((1,1),(1,2),(1,3),(1,4)...(n,n))
התרגיל היה כפוף למגבלה: אסור להשתמש בלולאות או ליצור "אפקט צד"…
התרגיל היה בScala אך בעקבות דיון בקבוצת הוואטסאפ DDOS הגעתי להצעות בכמה שפות,
אשתף:
Scala
הקוד כולו פונקציונאלי: המערך עובר מיפוי, עבור כל איבר מתקבל מערך המכיל זוגות זוגות של אברים, שהם עצמם נוצרו כתוצאה מZIP על זוג מערכים, הראשון הוא המערך המקורי עצמו, כולו, והשני הוא מערך המכיל את האיבר הראשון עצמו, משוכפל שוב ושוב עד אורך המערך המקורי, וכעת כל המערכים האלו עוברים שיטוח למערך יחיד.
הקוד נראה לי די קריא ותמציתי, אך כיוון שלמדתי לכתוב בScala בערך יומיים קודם, ייתכן שאפשר לקצרו יותר או לעשותו בהיר יותר.
Python
הקוד בפייתון ללא ספק ארוך יותר וקריא פחות, ראשית יוצרים מערך של מערכים, המכיל את המערך המקורי משוכפל שוב ושוב, ואב יוצרים מערך של מערכים המכיל את המערך המקורי כולו, משוכפל שוב ושוב, ואז משדכים אותך עם ZIP ואז עם MAP משדכים כל איבר לזוג זוג של אברים… ולבסוף עם SUM משטחים הכל למערך חדש.
כמה דברים מפריעים לי בקוד הזה, ראשית הוא ארוך ולא ממש אינטואיטיבי, שנית, בשורה 5 אנו נאלצים להמיר את הZIP לליסט, ובכך "להפעיל" את הZIP על כל האברים.
בלי זה נקבל שגיאה. אך זה מונע ייצור "עצל" של הזוגות ויהווה בעיה עבור מערך ארוך מאד.
ואחרון חביב, אני בעצם לא רוצה Set אלא List אבל נאלץ להשתמש בו כדי למנוע כפילות… אם בהמשך ארצה להשתמש בו כמערך, אצטרך להמיר לList. אולי מראש כדאי אפילו לעשות כן בעת ההחזרה מהפונקציה, ומ"מ זה מוסיף תקורה.
JavaScript
הקוד בJS מאד קצר ויפה, המערך עובר מיפוי מיוחד הכולל שיטוח, ומה שמשוטח הוא תוצאות Map עם Map פנימי, בכעין לולאה בתוך לולאה, היוצרת זוגות זוגות של המערך המקורי עם עצמו.
מסקנותיי
הקוד בJS ניצח בלי ספק, הן בקריאות הן באורך,מצד שני קשה לומר שזה תחרות אמיתית, המגבלה של העדר לולאות הכפויה, מונעת את ניצול היכולות האמיתות הן של Python הן של Scala…
לשם המחשה, עם לולאות בScala ניתן לכתוב ככה:
אבל במסגרת תחרות זו "עם ידיים קשורות מאחורי הגב" לדעתי JS ניצחה…
הערה: אני לצערי לא בקיא מאד בפייתון, למרות שאני אוהב מאד את השפה…
(המכללה בישלה דייסה, נתנה סימסטר אחד לC, סימסטר אחד לC++ ושני סימסטרים לJava (איכס…) ולפייתון לא נשאר… הלכה פייתון לחפש דייסה… ולא נשאר מאומה, הזמן שלי כבר נתפס כולו… )
כך שאולי לחינם דנתי את פייתון לחובה… אם למישהו יש פתרון יעיל וקריא יותר, אשמח לעמוד על טעותי.
תוספת על פי תגובות ידידיי הי"ו:
החכימני ידידי ר' דוד כץ, שיש לכך בפייתון פונקציה מובנית בספריה Itertools:
https://docs.python.org/3/library/itertools.html#itertools.product
אם נביא אותה בחשבון, פייתון כמובן מנצחת… אך מצד שני, "מה החכמה" כאן, הרי אחרי שכתבתי פונקציה, תמיד לקרוא לה זה מילה אחת… וכאן מאחורי הקלעים יש קוד הכולל לולאות… ובפועל הוא אפילו ארוך יותר ובC.