Обратная польская запись: примеры реализации
Примеры реализации
//Стандарт языка C++17
//Пример ввода: 8 9 + 1 7 - *
//______Реализация______
#include <iostream>
#include <string>
#include <functional>
#include <vector>
#include <map>
#include <cctype>
using namespace std;
int main()
{
map<char, function<int64_t(const int64_t&, const int64_t&)>> operations;
operations['+'] = [](const int64_t& a, const int64_t& b) constexpr { return a + b; };
operations['-'] = [](const int64_t& a, const int64_t& b) constexpr { return a - b; };
operations['*'] = [](const int64_t& a, const int64_t& b) constexpr { return a * b; };
operations['/'] = [](const int64_t& a, const int64_t& b) constexpr { return a / b; };
string expression;
vector<int64_t> stack_;
int64_t number = 0;
bool flag = true;
getline(cin, expression);
for (const auto& i : expression)
{
if (isdigit(i))
{
number *= 10;
number += (i - '0');
flag = true;
}
else
{
if (i != ' ')
{
int64_t num2 = stack_.back();
stack_.pop_back();
int64_t num1 = stack_.back();
stack_.pop_back();
stack_.push_back(operations[i](num1, num2));
flag = false;
}
else if (i == ' ' && flag)
{
stack_.push_back(number);
number = 0;
}
}
}
cout << stack_.back();
return 0;
}
3 2 1 + *
calc :: String -> [Float]
calc = foldl f [] . words
where
f (x:y:zs) "+" = (y + x):zs
f (x:y:zs) "-" = (y - x):zs
f (x:y:zs) "*" = (y * x):zs
f (x:y:zs) "/" = (y / x):zs
f xs y = read y : xs
-module(calc).
-export([expr/1]).
expr(L) ->
[Result] = lists:foldl(fun expr/2, [], string:tokens(L, " ")),
Result.
expr("+", [A, B | T]) -> [A + B | T];
expr("-", [A, B | T]) -> [A - B | T];
expr("*", [A, B | T]) -> [A * B | T];
expr("/", [A, B | T]) -> [A / B | T];
expr(N, T) -> [read(N) | T].
read(N) ->
case string:to_float(N) of
{error, no_float} -> list_to_integer(N);
{F, _} -> F
end.
/* Компиляция:
* gcc -o rpn rpn.c
*
* Использование:
* ./rpn <выражение>
*
* Пример:
* ./rpn 3 5 +
*
* Замечание: знак умножения `*' заменен на `x', т.к. символ `*' используется
* командной оболочкой.
**/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define STKDPTH 32 /* Глубина стека */
/* Значения, возвращаемые функцией parse */
#define VAL 0 /* В стек занесено новое значение */
#define ADD 1 /* Сложение */
#define SUB 2 /* Вычитание */
#define MUL 3 /* Умножение */
#define DIV 4 /* Деление */
#define SOF -1 /* Переполнение стека */
#define SUF -2 /* В стеке недостаточно операндов */
#define UNK -3 /* Неопознанное значение */
/* Глобальные переменные */
int scount;
double stack[STKDPTH];
/* Функция распознавания аргументов */
int parse(char *);
/* Точка входа */
int main(int argc, char **argv)
{
/* Организуем цикл для перебора аргументов командной строки */
while (*++argv) {
/* Пытаемся распознать текущий аргумент как число или
* символ арифметической операции */
switch (parse(*argv)) {
case VAL: continue;
/* Вычисляем */
case ADD:
stack[scount - 1] += stack[scount];
break;
case SUB:
stack[scount - 1] -= stack[scount];
break;
case MUL:
stack[scount - 1] *= stack[scount];
break;
case DIV:
if (stack[scount] != 0) {
stack[scount - 1] /= stack[scount];
break;
} else {
fprintf(stderr, "Деление на ноль!\n");
return(1);
}
/* Обработка ошибок */
case SUF:
fprintf(stderr, "Недостаточно операндов!\n");
return(1);
case SOF:
fprintf(stderr, "Переполнение стека!\n");
return(1);
case UNK:
fprintf(stderr, "Неопознанный аргумент!\n");
return(1);
}
}
/* Вывести результат */
auto int i;
for (i = 0; i < scount; i++) printf("%0.3f\n", stack[i]);
return(0);
}
int parse(char *s)
{
double tval = 0;
char * endptr;
/* Распознаем знаки арифметических операций */
switch (*s) {
case '-':
/* Если минус является первым и не последним символом аргумента,
* значит пользователь ввел отрицательное число и опознавать его
* как операцию вычитания не нужно */
if (*(s+1) != '\0') break;
if (scount >= 2) {
scount -= 1;
return(SUB);
}
else return(SUF);
case '+':
if (scount >= 2) {
scount -= 1;
return(ADD);
}
else return(SUF);
case 'x':
if (scount >= 2) {
scount -= 1;
return(MUL);
}
else return(SUF);
case '/':
if (scount >= 2) {
scount -= 1;
return(DIV);
}
else return(SUF);
}
errno = 0;
/* Пытаемся сконвертировать строковый аргумент в число */
tval = strtod(s, &endptr);
/* Вернуть ошибку `неопознанный аргумент' в случае неудачи */
if (errno != 0 || *endptr != '\0') return(UNK);
/* Проверяем, есть ли свободное место в стеке
* и сохраняем в нем операнд, иначе возвращаем ошибку переполнения */
if (scount < STKDPTH) stack[scount++] = tval;
else return(SOF);
return(VAL);
}
program calc;
{$APPTYPE console}
type
Real = double;
const
prs = '+-*/(';
pri: array [1 .. 5] of byte = (1, 1, 2, 2, 0);
var
s1, s2: String;
q: array [0 .. 500] of Real;
w: array [0 .. 500] of Char;
n, len, len2: Cardinal;
t: Real;
ch: Char;
procedure Push(x: Real);
begin
Inc(len);
q[len] := x;
end;
function Pop: Real;
begin
Pop := q[len];
q[len] := 0;
Dec(len);
end;
procedure PushC(x: Char);
begin
Inc(len2);
w[len2] := x;
end;
function Popc: Char;
begin
Popc := w[len2];
w[len2] := #0;
Dec(len2);
end;
function Oper(s1, s2: Real; s3: Char): Real;
var
x, y, z: Real;
begin
x := s1;
y := s2;
case s3 of
'+': z := x + y;
'-': z := x - y;
'*': z := x * y;
'/': z := x / y;
end;
Oper := z;
end;
procedure PreChange(var s: String);
var
i: Cardinal;
begin
if s[1] = '-' then
s := '0' + s;
i := 1;
while i <= n do
if (s[i] = '(') and (s[i + 1] = '-') then
insert('0', s, i + 1)
else
Inc(i);
end;
function Change(s: String): String;
var
i: Cardinal;
rezs: String;
c: Boolean;
begin
c := false;
for i := 1 to n do
begin
if not(s[i] in ['+', '-', '*', '/', '(', ')']) then
begin
if c then
rezs := rezs + ' ';
rezs := rezs + s[i];
c := false;
end
else
begin
c := true;
if s[i] = '(' then
PushC(s[i])
else
if s[i] = ')' then
begin
while w[len2] <> '(' do
begin
rezs := rezs + ' ' + Popc;
end;
Popc;
end
else
if s[i] in ['+', '-', '*', '/'] then
begin
while pri[Pos(w[len2], prs)] >= pri[Pos(s[i], prs)] do
rezs := rezs + ' ' + Popc;
PushC(s[i]);
end;
end;
end;
while len2 <> 0 do
rezs := rezs + ' ' + Popc;
Change := rezs;
end;
function Count(s: String): Real;
var
ss: String;
x, s1, s2: Real;
chh, s3: Char;
p, i, j: Cardinal;
tmp: Integer;
begin
i := 0;
repeat
j := i + 1;
repeat
Inc(i)
until s[i] = ' ';
ss := copy(s, j, i - j);
chh := ss[1];
if not(chh in ['+', '-', '*', '/']) then
begin
Val(ss, p, tmp);
Push(p);
end
else
begin
s2 := Pop;
s1 := Pop;
s3 := chh;
Push(Oper(s1, s2, s3));
end;
until i >= n;
x := Pop;
Count := x;
end;
procedure WriteL(x: Real);
var
y, a, b: Cardinal;
q: Real;
begin
y := Trunc(x);
b := 0;
if Abs(x - y) < (1E-12) then
Writeln(y)
else
begin
if y > 0 then
a := round(ln(y) / ln(10)) + 1
else
a := 1;
q := x;
repeat
q := q * 10;
Inc(b);
until Abs(q - Trunc(q)) < (1E-12);
Writeln(x:a + b:b);
end;
end;
begin
repeat
Writeln('Enter expression');
Readln(s1);
n := Length(s1);
PreChange(s1);
n := Length(s1);
s2 := Change(s1);
if s2[1] = ' ' then
delete(s2, 1, 1);
s2 := s2 + ' ';
n := Length(s2);
t := Count(s2);
WriteL(t);
Writeln('One more expression?(Y/N)');
Readln(ch);
until UpCase(ch) = 'N';
end.
ОТДЕЛ ОПН;
ПЕР
Стек: РЯД 20H ИЗ ЗНАК;
ЗАДАЧА Преимущество(зн: ЗНАК): ЦЕЛ;
УКАЗ
ВЫБРАТЬ зн ИЗ
'*', '/': ВОЗВРАТ 3
| '-', '+': ВОЗВРАТ 2
| '(', ')': ВОЗВРАТ 1
ИНАЧЕ
ВОЗВРАТ 0
КОН
КОН Преимущество;
ЗАДАЧА Добавить(зн: ЗНАК);
УКАЗ
ЕСЛИ ДЛИНА(Стек) = РАЗМЕР(Стек) ТО
Вывод.Цепь("Ошибка: стек переполнен.");
СТОП(0)
КОН;
Стек[ДЛИНА(Стек)] := зн
КОН Добавить;
ЗАДАЧА Удалить(): ЗНАК;
ПЕР
зн: ЗНАК;
УКАЗ
ЕСЛИ ДЛИНА(Стек) = 0 ТО
Вывод.Цепь("Ошибка: стек пуст.");
СТОП(0)
КОН;
зн := Стек[ДЛИНА(Стек)-1];
Стек[ДЛИНА(Стек)-1] := 0X;
ВОЗВРАТ зн
КОН Удалить;
ЗАДАЧА Перевести(вход-, выход+: РЯД ИЗ ЗНАК);
ПЕР
сч1, сч2: УЗКЦЕЛ;
зн: ЗНАК;
УКАЗ
зн := 0X; сч2 := 0;
ОТ сч1 := 0 ДО ДЛИНА(вход)-1 ВЫП
ЕСЛИ вход[сч1] = ")" ТО
ПОКА Стек[ДЛИНА(Стек)-1] # "(" ВЫП
выход[сч2] := Удалить();
УВЕЛИЧИТЬ(сч2)
КОН;
зн := Удалить()
АЕСЛИ вход[сч1] = "(" ТО
Добавить(вход[сч1])
АЕСЛИ (вход[сч1] = "+") ИЛИ (вход[сч1] = "-") ИЛИ (вход[сч1] = "/") ИЛИ (вход[сч1] = "*") ТО
ЕСЛИ ДЛИНА(Стек) = 0 ТО Добавить(вход[сч1]) ИНАЧЕ
ЕСЛИ Преимущество(вход[сч1]) > Преимущество(Стек[ДЛИНА(Стек)-1]) ТО
Добавить(вход[сч1])
ИНАЧЕ
ПОКА (ДЛИНА(Стек) # 0) И (Преимущество(вход[сч1]) <= Преимущество(Стек[ДЛИНА(Стек)-1])) ВЫП
выход[сч2] := Удалить(); УВЕЛИЧИТЬ(сч2)
КОН;
Добавить(вход[сч1])
КОН
КОН
АЕСЛИ Знак.Буква(вход[сч1]) ТО
выход[сч2] := вход[сч1];
УВЕЛИЧИТЬ(сч2)
КОН
КОН;
ПОКА ДЛИНА(Стек) # 0 ВЫП
выход[сч2] := Удалить();
УВЕЛИЧИТЬ(сч2)
КОН
КОН Перевести;
КОН ОПН.
Пример использования класса Calculate
<?php
require_once __DIR__ . '/Calculate.php';
$calc = new Calculate('3 * ((-25 - 10 * -2 ^ 2 / 4) * (4 + 5)) / 2');
if ($calc->postfixString) {
echo PHP_EOL;
echo 'Строка в постфиксной записи (~ - это унарный минус): ' . $calc->postfixString . PHP_EOL . PHP_EOL;
echo 'Результат вычисления постфиксной записи: ' . $calc->result . PHP_EOL . PHP_EOL;
} else {
echo $calc->result . PHP_EOL;
}
Листинг класса Calculate
<?php
/** @noinspection PhpIllegalPsrClassPathInspection */
class Calculate
{
private const UNARY_MINUS = '~';
private const OPEN_BRACKET = '(';
private const CLOSE_BRACKET = ')';
private const MINUS = '-';
private const PLUS = '+';
private const DIVISION = '/';
private const MULTIPLICATION = '*';
private const EXPONENTIATION = '^';
private const PRIORITY = [
self::OPEN_BRACKET => 0,
self::CLOSE_BRACKET => null,
self::PLUS => 2,
self::MINUS => 2,
self::MULTIPLICATION => 3,
self::DIVISION => 3,
self::EXPONENTIATION => 4,
self::UNARY_MINUS => 5
];
private const RIGHT_ASSOCIATIVE_EXPRESSION = [
self::EXPONENTIATION, self::UNARY_MINUS
];
private array $stack = [];
private array $outString = [];
/**
* @var float|string
*/
public $result;
public string $postfixString = '';
public function __construct(string $expression)
{
try {
preg_match('/-?\d+\s+-?\d+/', $expression, $matches);
if ($matches) {
throw new DomainException('Между числами нет оператора!');
}
$openBracket = substr_count($expression, self::OPEN_BRACKET);
$closeBracket = substr_count($expression, self::CLOSE_BRACKET);
if ($openBracket !== $closeBracket) {
throw new DomainException('Непарные скобки!');
}
$expression = preg_replace('/\s/', '', $expression);
$expression = str_replace(',', '.', $expression);
preg_match('/[^\d()+\/*-.^]+/', $expression, $matches);
if ($matches) {
throw new DomainException('Ошибка! В строке могут быть только цифры, скобки, и операторы +, -, *, /, ^');
}
$this->createOutString($expression);
$this->postfixString = implode(' ', $this->outString);
$this->result = $this->calcFromOutString();
} catch (Exception $e) {
$this->result = $e->getMessage();
}
}
private function calc($left, $right, $operator)
{
switch ($operator) {
case self::MINUS:
return $left - $right;
case self::PLUS:
return $left + $right;
case self::MULTIPLICATION:
return $left * $right;
case self::EXPONENTIATION:
return $left ** $right;
case self::DIVISION:
if ($right == 0) {
throw new DomainException('Деление на ноль!');
}
return $left / $right;
default:
throw new DomainException('Неизвестный оператор ' . $operator);
}
}
/**
* приводим к постфиксной записи
*/
private function createOutString(string $expression)
{
$length = strlen($expression) - 1;
$number = null;
for ($i = 0; $i <= $length; $i++) {
$item = $expression[$i];
$left = $i === 0 ? null : $expression[$i - 1];
$right = $i === $length ? null : $expression[$i + 1];
if ($item === '-') {
$arr = [self::PLUS, self::MULTIPLICATION, self::EXPONENTIATION, self::MINUS, self::DIVISION, self::OPEN_BRACKET];
if ($left === null || in_array($left, $arr)) {
$item = self::UNARY_MINUS;
}
}
if (is_numeric($item) || $item === '.') {
if ($item === '.') {
if ($left === null || $right === null || !is_numeric($left) || !is_numeric($right)) {
throw new DomainException('Неверное дробное выражение!');
}
}
$number .= $item;
if (!is_numeric($right)) {
$this->outString[] = (float)$number;
$number = null;
}
continue;
}
if (in_array($item, array_keys(self::PRIORITY))) {
if ($item === self::OPEN_BRACKET && is_numeric($left)) {
$this->addToStackAndPushFromStack(self::MULTIPLICATION);
}
$this->addToStackAndPushFromStack($item);
if ($item === self::CLOSE_BRACKET && (is_numeric($right) || $right === self::OPEN_BRACKET)) {
$this->addToStackAndPushFromStack(self::MULTIPLICATION);
}
}
}
while ($this->stack) {
$this->outString[] = array_pop($this->stack);
}
}
private function addToStackAndPushFromStack(string $operator)
{
if (!$this->stack || $operator === self::OPEN_BRACKET) {
$this->stack[] = $operator;
return;
}
$stack = array_reverse($this->stack);
if ($operator === self::CLOSE_BRACKET) {
foreach ($stack as $key => $item) {
unset($stack[$key]);
if ($item === self::OPEN_BRACKET) {
$this->stack = array_reverse($stack);
return;
}
$this->outString[] = $item;
}
}
foreach ($stack as $key => $item) {
if (in_array($item, self::RIGHT_ASSOCIATIVE_EXPRESSION) && $item === $operator) {
break;
}
if (self::PRIORITY[$item] < self::PRIORITY[$operator]) {
break;
}
$this->outString[] = $item;
unset($stack[$key]);
}
$this->stack = array_reverse($stack);
$this->stack[] = $operator;
}
/**
* @return float
* Вычисляем из постфиксной записи
*/
private function calcFromOutString(): float
{
$stack = [];
foreach ($this->outString as $item) {
if (is_float($item)) {
$stack[] = $item;
continue;
}
if ($item === self::UNARY_MINUS) {
$last = array_pop($stack);
if (!is_numeric($last)) {
throw new DomainException('Неверное выражение!');
}
$stack[] = 0 - $last;
continue;
}
$right = array_pop($stack) ?? null;
$left = array_pop($stack) ?? null;
if ($right === null || $left === null) {
throw new DomainException('Неверное выражение!');
}
$stack[] = $this->calc($left, $right, $item);
}
return $stack[0];
}
}
Функция toRPN работает некорректно. Можно проверить на формуле 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3.
Должно быть: 3 4 2 * 1 5 − 2 3 ^ ^ / +
Функция отдаёт: 3 4 2 * 1 5 - 2 ^ 3 ^ / +.
Да нет, как раз результат 3 4 2 * 1 5 - 2 ^ 3 ^ / + является верным
Некорректно работает для Input = "5"
// Преобразование инфиксной записи к постфиксной
// i.dudinov@gmail.com
// Добавлена функция result для вывода результата
// kiss_a@bk.ru
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace PostfixNotation
{
public class PostfixNotationExpression
{
public PostfixNotationExpression()
{
operators = new List<string>(standart_operators);
}
private List<string> operators;
private List<string> standart_operators =
new List<string>(new string[] { "(", ")", "+", "-", "*", "/", "^" });
private IEnumerable<string> Separate(string input)
{
int pos = 0;
while (pos < input.Length)
{
string s = string.Empty + input[pos];
if (!standart_operators.Contains(input[pos].ToString()))
{
if (Char.IsDigit(input[pos]))
for (int i = pos + 1; i < input.Length &&
(Char.IsDigit(input[i]) || input[i] == ',' || input[i] == '.'); i++)
s += input[i];
else if (Char.IsLetter(input[pos]))
for (int i = pos + 1; i < input.Length &&
(Char.IsLetter(input[i]) || Char.IsDigit(input[i])); i++)
s += input[i];
}
yield return s;
pos += s.Length;
}
}
private byte GetPriority(string s)
{
switch (s)
{
case "(":
case ")":
return 0;
case "+":
case "-":
return 1;
case "*":
case "/":
return 2;
case "^":
return 3;
default:
return 4;
}
}
public string[] ConvertToPostfixNotation(string input)
{
List<string> outputSeparated = new List<string>();
Stack<string> stack = new Stack<string>();
foreach (string c in Separate(input))
{
if (operators.Contains(c))
{
if (stack.Count > 0 && !c.Equals("("))
{
if (c.Equals(")"))
{
string s = stack.Pop();
while (s != "(")
{
outputSeparated.Add(s);
s = stack.Pop();
}
}
else if (GetPriority(c) > GetPriority(stack.Peek()))
stack.Push(c);
else
{
while (stack.Count > 0 && GetPriority(c) <= GetPriority(stack.Peek()))
outputSeparated.Add(stack.Pop());
stack.Push(c);
}
}
else
stack.Push(c);
}
else
outputSeparated.Add(c);
}
if (stack.Count > 0)
foreach (string c in stack)
outputSeparated.Add(c);
return outputSeparated.ToArray();
}
public decimal result(string input)
{
Stack<string> stack = new Stack<string>();
Queue<string> queue = new Queue<string>(ConvertToPostfixNotation(input));
string str = queue.Dequeue();
while (queue.Count >= 0)
{
if (!operators.Contains(str))
{
stack.Push(str);
str = queue.Dequeue();
}
else
{
decimal summ = 0;
try
{
switch (str)
{
case "+":
{
decimal a = Convert.ToDecimal(stack.Pop());
decimal b = Convert.ToDecimal(stack.Pop());
summ = a + b;
break;
}
case "-":
{
decimal a = Convert.ToDecimal(stack.Pop());
decimal b = Convert.ToDecimal(stack.Pop());
summ=b-a;
break;
}
case "*":
{
decimal a = Convert.ToDecimal(stack.Pop());
decimal b = Convert.ToDecimal(stack.Pop());
summ = b * a;
break;
}
case "/":
{
decimal a = Convert.ToDecimal(stack.Pop());
decimal b = Convert.ToDecimal(stack.Pop());
summ = b / a;
break;
}
case "^":
{
decimal a = Convert.ToDecimal(stack.Pop());
decimal b = Convert.ToDecimal(stack.Pop());
summ = Convert.ToDecimal(Math.Pow(Convert.ToDouble(b), Convert.ToDouble(a)));
break;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
stack.Push(summ.ToString());
if (queue.Count > 0)
str = queue.Dequeue();
else
break;
}
}
return Convert.ToDecimal(stack.Pop());
}
}
}
# Преобразование к польской записи
# <oskin@rambler.ru>
def opz(iStr, stack)
priority = Hash["(" => 0, "+" => 1, "-" => 1, "*" => 2, "/" => 2, "^" => 3]
case iStr
when /^\s*([^\+\-\*\/\(\)\^\s]+)\s*(.*)/ then $1 + " " + opz($2, stack)
when /^\s*([\+\-\*\/\^])\s*(.*)/
if (stack.empty? or priority[stack.last] < priority[$1]) then opz($2, stack.push($1))
else stack.pop + " " + opz(iStr, stack) end
when /^\s*\(\s*(.*)/ then opz($1, stack.push("("))
when /^\s*\)\s*(.*)/
if stack.empty? then raise "Error: Excess of closing brackets."
elsif priority[head = stack.pop] > 0 then head + " " + opz(iStr, stack)
else opz($1, stack) end
else if stack.empty? then ""
elsif priority[stack.last] > 0 then stack.pop + " " + opz(iStr, stack)
else raise "Error: Excess of opening brackets." end
end
end
while a = gets
begin
puts opz(a, [])
rescue Exception => e
puts e.message
end
end
const operators = {
'+': (x, y) => x + y,
'-': (x, y) => x - y,
'*': (x, y) => x * y,
'/': (x, y) => x / y
};
let evaluate = (expr) => {
let stack = [];
expr.split(' ').forEach((token) => {
if (token in operators) {
let [y, x] = [stack.pop(), stack.pop()];
stack.push(operators[token](x, y));
} else {
stack.push(parseFloat(token));
}
});
return stack.pop();
};
import operator
OPERATORS = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv}
def polska(srt):
stack = []
lst = list(srt)
for i in srt:
if i.isdigit():
stack.append(i)
lst.remove(i)
else:
cnt1, cnt2 = stack.pop(), stack.pop()
stack.append(OPERATORS[i](int(cnt2), int(cnt1)))
lst.remove(i)
return stack.pop()