[[oktatas:programozás:algoritmusok|< Algoritmusok]] ====== Lengyelforma ====== * **Szerző:** Sallai András * Copyright (c) 2014, Sallai András * Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC BY-SA 4.0]] * Web: https://szit.hu ===== A lengyelformáról ===== A lengyelforma, angolul polish notation, **Jan Łukasiweicz** matematikus munkája, amit az 1920-as években kutatott. A lengyelforma az aritmetikai kifejezések felírása olyan formában, hogy az operátorok az operandusokat megelőzi. A 3 + 2 helyett a következőt írom: + 3 2. Az informatikában a fordított lengyelformát használjuk, vagyis az operátorokat az operandusok után írjuk: 3 2 +. A fordított lengyelforma angolul **Reverse Polish Notation**, röviden **RPN**, amit angol nyelv területen gyakran használnak. A magyar nyelvben általában csak lengyelformaként említik. Az aritmetikai kifejezések hagyományos írásának az elnevezése **infix** forma. Ha az operátorokat az operandusok elé írjuk, akkor **prefix** formáról beszélünk. Ha az operátorokat az operandusok után írjuk, akkor **postfix** formáról beszélünk. ^ Infix jelölés ^ | 3 + 2 | ^ Prefix jelölés ^ | + 3 2 | ^ Postfix jelölés ^ | 3 2 + | {{:oktatas:programozás:algoritmusok:lengyelforma.png|}} ===== A lengyelforma és a zárójelek ===== A lengyelformában nem használunk zárójeleket. A sorrend meghatározza a műveletek sorrendjét. Ránézésre ez kissé átláthatatlanabb, de a számítógép számára könnyebben feldolgozható, és kevés művelet esetén jobb feldolgozási időt produkál. A következő példákban a zárójelek eltűnését láthatjuk a postfix formában: * infix: 2 * ( 2 + 1 ) ^ 2 * postfix: 2 2 1 + 2 ^ * ===== Az algoritmusról ===== A lengyelformára konvertáló legnevesebb algoritmus angolul Shunting-yard, ami pályaudvarnak fordítható. Lengyelforma kiértékelő algoritmust evalRPN néven találjuk meg. Mindkét algoritmust veremmel valósítjuk meg. A következő képen a Shuting-yard algoritmust látjuk működés közben: {{:oktatas:programozás:algoritmusok:shunting_yard_magyar.png|}} ===== Java megvalósítás ===== ==== Lengyelformára konvertálás ==== static String infixToPostfix(String infix) { final String operators = "-+/*^"; StringBuilder output = new StringBuilder(); Stack verem = new Stack(); StringTokenizer infixTokens = new StringTokenizer(infix, " "); while (infixTokens.hasMoreTokens()) { String token = infixTokens.nextToken(); //Ha operátor if(operators.contains(token)) { //Ha üres a verem, akkor az operátor megy a verembe if (verem.isEmpty()) { verem.push(token); }else { // Ha nem üres, megnézzük mi a prioritása // a verem tetején lévőhöz képest while (!verem.isEmpty()) { int priorityTopStack = operators.indexOf(verem.peek()); int priorityToken = operators.indexOf(token); // Ha verem tetején az operátornak nagyobb prioritása, // akkor előbb azt fűzzük a kimenethez if (priorityTopStack >= priorityToken){ output.append(verem.pop()); output.append(' '); } else break; } verem.push(token); } } else if(token.equals("(")) { //Szimpla nyitó zárójelet a verembe tesszük verem.push(token); } else if(token.equals(")")) { // Ha bezáró zárójelet találunk, akkor a vermet // a kimenetre ürítjük egy nyitózárójelig while (!verem.peek().equals("(")) { output.append(verem.pop() + " "); } verem.pop(); //zárójelet eldobjuk } else { output.append(token + " "); } } // Végül kiürítjük a vermet, // ha még van benne valami, a kimenetre while (!verem.isEmpty()) { output.append(verem.pop() + " "); } return output.toString(); } ==== Érték számítása ==== static double evalPostfix(String postfix) { final String operators = "-+/*^"; Double result = 0.0; Stack stack = new Stack(); StringTokenizer postfixTokens = new StringTokenizer(postfix, " "); while(postfixTokens.hasMoreTokens()) { String token = postfixTokens.nextToken(); if(operators.contains(token)) { double num2 = Double.parseDouble(stack.pop()); double num1 = Double.parseDouble(stack.pop()); switch (token) { case "+" : result = num1 + num2; break; case "-" : result = num1 - num2; break; case "/" : result = num1 / num2; break; case "*" : result = num1 * num2; break; case "^" : result = Math.pow(num1, num2); break; } stack.push(result.toString()); }else { stack.push(token); } } return result; } ===== Kifejezés tárolása bináris fában ===== A lengyelformát tárolhatjuk fában {{:oktatas:programozás:algoritmusok:lengyelforma_faban.png|}} A levelek szintjén találjuk a operandusokat, a csúcsokban pedig a műveleteket. A fában nem tárolunk zárójeleket, mégis egyértelmű lehet, melyik műveletet kell elsőként elvégezni. A következő táblázatokban a előbbi ábra két fájának bejárásával kapott kifejezéseket láthatjuk: ^ preorder bejárás ^^ ^ bal oldali ^ jobb oldali ^ | - 8 * 2 3 | * - 8 2 3 | ^ inorder bejárás ^^ ^ bal oldali ^ jobb oldali ^ | 8 - 2 * 3 | 8 - 2 * 3 | ^ postorder bejárás ^^ ^ bal oldali ^ jobb oldali ^ | 8 2 3 * - | 8 2 - 3 * | Az eredményekből látható, hogy az inorder bejárás alkalmatlan a kifejezés egyértelmű visszafejtésére.