חידה (שפת C)

הצלוי

New member
חידה (שפת C)

הנה חידה שאהבתי
היה פעם במבחן בקורס C באוניברסיטה הפתוחה: תכתבו תוכנית שמקבלת מספר שלם אי זוגי ומציירת את הצורה הבאה (הציור בדוגמא מתאים עבור n=9): ********* ******* ***** *** * *** ***** ******* ********* (אורך השורה הראשונה והאחרונה היא n) והcatch הוא: בעזרת לולאה אחת!!
מי שמצליח
 

הצלוי

New member
../images/Emo4.gif לא יצא טוב. שוב:

********* ******* ***** *** * *** ***** ******* *********​
 

זויברג

New member
אני לא מבין ממש איפה הcatch?

אתה עושה לולאה של n, יש לך משתנה אחד, אתה מציב בתוכו את n, כל פעם מוריד ממנו שתים, ומצייר את הקו בערך אבסולוטי, ובעזרת התניה, מדלג על השלב בו x = -1 דוגמא:
n = 5 x = n = 5 x = 3 x = 1 x = -1 x = -3 x = -5​
 

ברנדל

New member
מב הבעיה?

נניח קיבלת n. אז הלולאה שלך תנוע מ n עד ל -n, בהפרשים של 2 , ותתיחס לערך האסבולוטי של המספר
 

דריזט

New member
פתרון

מכיוון שהתוכנית מחייבת לפחות שתי פעולות לולאתיות, ואנו מחויבים ללואה אחת, נגיע למסקנה שהדרך הפשוטה ביותר תהיה להשתמש בללואה אינסופית. מכאן הדרך כבר יותר פשוטה.
void starstriangle (int num) { assert (num % 2 == 1 && num > 0); int stars = num, spaces=0; const int orignum = num; while(1) { if ((orignum-abs(num))/2 > spaces) { spaces++; putchar (' '); } else { putchar('*'); stars--; } if (stars==0) { putchar('\n'); if (num == -orignum ) break; num -= 2; if (num == -1) { num = -3; } stars = abs(num); spaces=0; } } }​
את השאלה הזאת פתרתי לאחר תיקון קטן שמצאתי בהרצה הראשונה. בנושא הזה, אני רוצה לפתוח דיון על מתכונת המבחנים - הדרישה לתכנת על הנייר. האם הדרישה הזאת מוצדקת? בררור שמתכנת שיכול למצוא טעויות בעצמו טוב יותר בצורה מסוימת ממתכנת שנעזר במחשב, אבל עד כמה הדבר חשוב לכלל עבודתו?
 

vinney

Well-known member
מוצדקת

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

דריזט

New member
נכון

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

vinney

Well-known member
תרשה לי

break משמש לבריחה מלולאה/switch. אם ב switch חייבים להשתמש בו, אז לגבי לולאות - בדרך כלל השימוש נובע ממצבים לא מחושבים (שגיאות/exceptions וכד') שגורמים לך לעצור את המהלך התקין ולצאת החוצה במקרה שלך שמת break בלולאה שיכולת למצוא לה תנאי עצירה ברור, קריא וצפוי כבר באתחול הלולאה, ולכן במבחן היו מורידים לך לפחות 5% מהניקוד על השאלה, על אף שהפתרון שלך הוא נכון מתמטית. וזאת דעתי.
 

הצלוי

New member
פתרון ממש יפה:)

אפילו יותר טוב מהפיתרון המקורי שהציעו. הפיתרון המקורי היה:
void starstriangle(int n) { int i,j; #define STARS "********************************************" for (i=0, j=n-1; j>=0; i++, j--) printf("%*.*s\n", i<j?j+1:i+1, abs(i-j)+1, STARS); }​
הבעיה בפיתרון המקורי זה שהגודל של STARS הוא קבוע, ולכן הוא לא יעבוד על מספרים גדולים מהגודל של STARS (בניגוד לפיתרון שלך).
 

דריזט

New member
הפתרון הזה מזכיר לי

תחרויות obfuscated code למיניהן
אתה יכול להסביר לי את הפורמט שמועבר ל printf? והאם יש סיבה לכך שהכוכבים מוגדרים כ define ולא כ char[]?
 

הצלוי

New member
הסבר:

קודם כל, הסיבה היא שהכוכבים הם קבוע, לכן הם מוגדרים כdefine. זה לא עקרוני בכל מקרה... הפורמט שמועבר לprintf הוא כזה:
%*.*s​
מחליפים כל כוכבית במשתנה הבא (כמו עם המחרוזת). הכוכבית הראשונה אומרת את גודל המחרוזת המינימלי שיודפס (אם למשל גודל המחרוזת היא 3 והגודל המינימלי הוא 5 אז יוסיפו 2 רווחים לפני המחרוזת). הכוכבית השנייה אומרת כמה תווים מהמחרוזת יודפסו (כמה כוכביות יודפסו במקרה שלנו). בכוכבית הראשונה שמים את j+1 או את i+1 (תלוי מי גדול יותר), ובכוכבית השנייה שמים את ההפרש שלהם... וזה כבר מתמטיקה
תנסה להריץ את זה על יבש...
 

vinney

Well-known member
פתרון קלאסי לC ../images/Emo13.gif

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