מבוא למדעי המחשב שימוש במחסנית - מחשבון
תוכן עניינים prefix כתיבת ביטויים ב-,infix ו- postfix
postfix prefix,infix ביטויים ב- כתיבת ו- infix נוסח כתיבה ב- (operator אנו רגילים לכתוב ביטויים חשבוניים כדוגמת 5+6 הארגומנטים (ה- (operands כאשר הפעולה (ה- נמצאת בין שני ישנם שני נוסחי כתיבה נוספים: (polish notation גם (נקרא prefix נוסח כתיבה ב- שבו הפעולה נכתב לפני הארגומנטים, למשל sin 5 (reverse polish notation גם (נקרא postfix נוסח כתיבה ב-!n שבו הפעולה נכתב לאחר הארגומנטים, למשל
עץ חישוב (aa bb + cc ) * ניתן לרשום כל ביטוי חשבוני באמצעות עץ החישוב נעשה מלמטה למעלה (עץ הביטוי) סריקה שונה של העץ תניב נוסח כתיבה שונה של הביטוי a b + c סריקה נוסח ביטוי in-order infix (a * (b + c)) pre-order prefix (* a (+ b c)) post-order postfix (a (b c +) *)
תוכן עניינים postfix prefix כתיבת ביטויים ב-,infix ו- postfix חישוב ביטויים ב-
חישוב של ביטוי ב- infix למרות שאנו רגילים לרשום ביטויים ב-,infix אלו ביטויים שקשה "לפענח" באופן ממוחשב לדוגמה: 9 11)) + 3) (7 + 4) ((3 + 2 (5 + החישוב מונחה ע"י הסוגריים וסדר פעולות החשבון אנחנו כה מורגלים בפירוק הביטויים הללו עד שאנו עשויים להבחין בקושי רק כאשר מדובר בביטויים ארוכים במיוחד אולי יש אפשרות אחרת? סריקה נוסח ביטוי in-order infix (a * (b + c)) pre-order prefix (* a (+ b c)) post-order postfix (a (b c +) *)
חישוב של ביטוי ב- postfix מתברר שאם הביטוי כתוב ב-,postfix יש דרך מאוד פשוטה לחשב אותו אבל, בדרך כלל אנחנו כותבים ביטויים ב-,infix אז מה זה עוזר לנו? (מיד בהמשך) קל יחסית גם להפוך ביטויים ב- infix לביטויים ב- postfix למשל ע"י יצירה של עץ הביטוי ומעבר על העץ ב- postorder או גם בדרך אחרת פשוטה יחסית, באמצעות מחסניות סריקה נוסח ביטוי in-order infix (a * (b + c)) pre-order prefix (* a (+ b c)) post-order postfix (a (b c +) *)
סוגריים להבדיל מביטויים ב- infix ביטויים ב- prefix או ב- postfix סדר הפעולות נקבע לפי המיקום היחסי שלהם בביטוי לא זקוקים לסוגריים סריקה נוסח ביטוי in-order infix (a * (b + c)) pre-order prefix * a + b c post-order postfix a b c + *
- הרעיון חישוב של ביטוי ב- postfix 5 2 3 4 + 7 3 + 11 + + 9 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 5 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 2 5 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 3 2 5 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 4 3 2 5 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 7 2 5 4 3 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 7 7 2 5 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 3 7 7 2 5 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 3 10 7 2 5 7 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 70 2 5 10 7 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 11 70 2 5 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 81 2 5 11 70 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 נעבור על איברי הביטוי 162 5 81 2 כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית 167 162 5 כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 9 167 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
הרעיון 5 2 3 4 + 7 3 + 11 + + 9 נעבור על איברי הביטוי כאשר נתקל בערך מספרי נכניס אותו למחסנית 1503 9 167 כאשר נתקל באופרטור בסוף החישוב נפעיל אותו על שני הערכים המספריים העליונים במחסנית ונכניס את התוצאה למחסנית נשאר עם איבר אחד (התוצאה) במחסנית ונחזיר אותו
תוכן עניינים postfix prefix כתיבת ביטויים ב-,infix ו- postfix חישוב ביטויים ב- מימוש
- תזכורת הממשק Stack public interface Stack<T> { boolean isempty(); T pop(); void push(t element); } T peek();
המימוש נכין את הביטוי כמערך של Object םי- מספרים נייצג כ- Integer ופעולות כמחרוזות עם סימן הפעולה 5 2 3 4 + 7 3 + 11 + + 9 למשל את הביטוי נארוז במערך "} " 9, ", + " ", " ", + " 11, ", " ", + " 3, ", 7, + " 4, {5, 2, 3, נכתוב פונקציה מהצורה exp) eeeeeeeeeeeeeeee(oooooooooooo [] שתחשב את הביטוי
הפונקציה eveluate public static int evaluate(object[] postfixexpr) { Stack<Integer> st = new StackAsDynamicArray<>(); for (Object item : postfixexpr) { if (item instanceof Integer) st.push((integer) item); else if (item instanceof String) { Integer rhs = st.pop(); Integer lhs = st.pop(); if (item.equals("+")) st.push(lhs + rhs); else if (item.equals("*")) st.push(lhs * rhs); else throw new IllegalStateException(); } else throw new IllegalStateException(); } Integer ans = st.pop(); if (!st.isempty()) throw new IllegalStateException(); return ans; }
דוגמת הרצה Object[] postfixexpr1 = { 5, 2, 3, 4, "+", 7, 3, "+", "*", 11, "+", "*", "+", 9, "* }; System.out.println("value: " + evaluate(postfixexpr1)); // value: 1503
תוכן עניינים postfix prefix כתיבת ביטויים ב-,infix ו- postfix חישוב ביטויים ב- מימוש מימוש נוסף
למה צריך מימוש נוסף? המימוש שהצענו אינו ניתן להרחבה בקלות כדי להוסיף למשל את פעולת החיסור והחילוק, אנחנו צריכים להוסיף קוד לקוד הקיים היינו רוצים מימוש שבו נוכל להוסיף פעולות כרצוננו מבלי לחזור ולשנות את הקוד הקיים
הרעיון נגדיר אופרטורים כאובייקטים למשל האופרטור של החיבור או האופרטור של הכפל, כל אחד מהם יוגדר כאובייקט נגדיר בכל אובייקט כזה שיטה, שמקבלת כפרמטר מחסנית, ויודעת לבצע את הפעולה הנדרשת על המחסנית למשל, באופרטור החיבור, השיטה תשלוף שני מספרים מהמחסנית תפעיל עליהם את פעולת החיבור ותדחוף את התוצאה למחסנית כדי להיות פשוטים, נגדיר גם את המספרים כאופרטורים שדוחפים את הערך של עצמם לתוך המחסנית את הביטוי נארוז כמערך של אופרטורים ונכתוב פונקציה שמקבלת כפרמטר ביטוי כמערך של אופרטורים, מקצה מחסנית, עוברת על האופרטורים ומפעילה אותם על המחסנית, ומחזירה את הערך האחרון שנשאר במחסנית
הממשק StackOperator public interface StackOperator { void apply(stack<integer> stack); } String tostring();
המחלקה IntegerOperator public class IntegerOperator implements StackOperator { private Integer value; public IntegerOperator(int value) { this.value = value; } public void apply(stack<integer> stack) { stack.push(value); } public String tostring() { return value.tostring(); } }
המחלקה IntPlusOperator public class IntPlusOperator implements StackOperator { public void apply(stack<integer> stack) { Integer rhs = stack.pop(); Integer lhs = stack.pop(); stack.push(lhs + rhs); } public String tostring() { return "+"; } }
המחלקה IntMultOperator public class IntMultOperator implements StackOperator { public void apply(stack<integer> stack) { Integer rhs = stack.pop(); Integer lhs = stack.pop(); st.push(lhs * rhs); } public String tostring() { return "*"; } }
השיטה evaluate public static int evaluate(stackoperator[] postfixexpr) { Stack<Integer> stack = new StackAsDynamicArray<>(); for (StackOperator op : postfixexpr) op.apply(stack); Integer ans = stack.pop(); if (!stack.isempty()) throw new IllegalArgumentException(); return ans; }
דוגמת הרצה StackOperator[] postfixexpr2 = { new IntegerOperator(5), new IntegerOperator(2), new IntegerOperator(3), new IntegerOperator(4), new IntPlusOperator(), new IntegerOperator(7), new IntegerOperator(3), new IntPlusOperator(), new IntMultOperator(), 5 2 3 4 + 7 3 + 11 + + 9 new IntegerOperator(11), new IntPlusOperator(), new IntMultOperator(), new IntPlusOperator(), new IntegerOperator(9), new IntMultOperator() }; System.out.println("value: " + evaluate(postfixexpr2)); // value: 1503
דוגמת הרצה StackOperator[] postfixexpr2 = { new IntegerOperator(2), new IntegerOperator(3), 2 3 + new IntPlusOperator() }; System.out.println("value: " + evaluate(postfixexpr2)); // value: 1503 postfixexpr[0].apply(st) postfixexpr[1].apply(st) postfixexpr[2].apply(st) 2 3 2 5 3 2
תוכן עניינים כתיבת ביטויים ב- prefix,infix ו- postfix חישוב ביטויים ב- postfix מימוש מימוש נוסף המרה של ביטויים למערכים של אופרטורים
המרה של ביטויים כיצד להפוך ביטוי הנתון באמצעות מחרוזת למערך של אופרטורים? public class ExpressionParser { private static List<StackOperator> operators =...; public static StackOperator[] parse(string str) {... } } private static boolean isinteger(string str) {... }
אתחול המערך עם האופרטורים הרלוונטיים private static List<StackOperator> operators = Arrays.asList(new IntPlusOperator(), new IntMultOperator());
הפונקציה parse public static StackOperator[] parse(string str) { String[] tokens = str.split(" "); StackOperator[] postfixexpr = new StackOperator[tokens.length]; for (int i = 0; i < tokens.length; i = i + 1) { String token = tokens[i]; if (isinteger(token)) postfixexpr[i] = new IntegerOperator(new Integer(token)); else { StackOperator tokenoperator = null; for (StackOperator operator : operators) if (token.equals(operator.tostring())) tokenoperator = operator; if (tokenoperator == null) throw new IllegalArgumentException(); postfixexpr[i] = tokenoperator; } } return postfixexpr; }
הפונקציה isinteger private static boolean isinteger(string str) { // regular expression // X? X, once or not at all // \d A digit: [0-9] // X+ X, one or more times // String expression // \\ The backslash character } // with or without minus sign and then at lest one digit return str.matches("-?\\d+");