Цель работы: изучить правила формирования постфиксной записи ариф— метических выражений с использованием стека.
4.1. Краткие теоретические сведения
Одной из задач при разработке трансляторов является задача расшифров— ки арифметических выражений.
Выражение a + b записано в инфиксной, +ab – в префиксной, ab+ – в постфиксной формах. В наиболее распространенной инфиксной форме для ука— зания последовательности выполнения операций необходимо расставлять скобки.
Польский математик Я. Лукашевич использовал тот факт, что при записи постфиксной формы скобки не нужны, а последовательность операндов и опе— раций удобна для расшифровки.
Постфиксная запись выражений получила название обратной польской записи (ОПЗ). Например, в ОПЗ выражение
(a + b) * (c + d) – e
выглядит следующим образом:
ab + cd + * e – .
Алгоритм преобразования выражения из инфиксной формы в форму ОПЗ с использованием стекового приоритета операций был предложен Э. Дейкстрой.
Рассмотрим алгоритм получения ОПЗ из исходной строки символов, в которой записано выражение в инфиксной форме.
1.Символы—операнды переписываются в выходную строку, в которой формируется постфиксная форма выражения.
2.Открывающая скобка записывается в стек.
3.Очередная операция «выталкивает» в выходную строку все операции из стека с большим или равным приоритетом, после чего записывается в стек.
4.Закрывающая скобка «выталкивает» все операции из стека до ближай— шей открывающей скобки в выходную строку, открывающая скобка удаляется из стека, а закрывающая – игнорируется.
5.Если после просмотра последнего символа исходной строки в стеке ос— тались операции, то все они «выталкиваются» в выходную строку.
4.2. Пример выполнения задания
Написать программу расшифровки и вычисления арифметических выра— жений с использованием стека.
Вид формы и полученные результаты представлены на рис. 4.1.
25
Рис. 4.1
Приведем только тексты используемых функций—обработчиков и создан— ных функций пользователя (тексты функций InStack и OutStack взять в лаб. ра— боте №2, заменив тип int на char):
. . .
struct Stack { char info; Stack *next;
} *begin;
int Prior (char);
Stack* InStack( Stack*,char); Stack* OutStack( Stack*,char*); double Rezult(String);
double mas[201]; |
// Массив для вычисления |
|
Set |
<char, 0, 255> znak; |
// Множество символов—знаков |
int Kol = 8; |
||
//——————— |
Текст функции—обработчика FormCreate ————————— |
|
Edit1->Text = «a+b*(c-d)/e»; |
Edit2->Text = «»; |
char a = ‘a’;
StringGrid1->Cells[0][0] =»Имя«; StringGrid1->Cells[1][0] =»Знач.»;
for(int i = 1; i<Kol; i++) { |
StringGrid1->Cells[1][i] = i; |
|
StringGrid1->Cells[0][i] = a++; |
||
} |
Текст функции—обработчика кнопки Перевести |
|
//———————- |
||
Stack *t; |
// Стек операций пуст |
|
begin = NULL; |
||
char ss, a; |
// Входная и выходная строки |
|
String InStr, OutStr; |
||
OutStr = «»; |
Edit2->Text = «»; |
InStr = Edit1->Text;
znak << ‘*’ << ‘/’ << ‘+’ << ‘-‘ << ‘^’; int len = InStr.Length(), k;
for (k = 1; k <= len; k++) {
ss= InStr[k];
//Открывающую скобку записываем в стек
if ( ss == ‘(‘ ) begin = InStack(begin, ss); if ( ss == ‘)’ ) {
26
// Выталкиваем из стека все знаки операций до открывающей скобки
while ( (begin -> info) != ‘(‘ ) { |
// Считываем элемент из стека |
begin = OutStack(begin,&a); |
|
OutStr += a; |
// Записываем в строку |
} |
// Удаляем из стека скобку «(» |
begin = OutStack(begin,&a); |
}
// Букву (операнд) заносим в выходную строку
if (ss >= ‘a’ && ss <= ‘z’ ) OutStr += ss;
/* Если знак операции, то переписываем из стека в выходную строку все опера— ции с большим или равным приоритетом */
if (znak.Contains(ss)) {
while ( begin != NULL && Prior (begin -> info) >= Prior (ss) ) { begin = OutStack(begin, &a);
OutStr += a;
}
begin = InStack(begin, ss);
}
}
// Если стек не пуст, переписываем все операции в выходную строку while ( begin != NULL){
begin = OutStack(begin, &a);
OutStr += a; |
||||
} |
// Выводим полученную строку |
|||
} |
Edit2->Text = OutStr; |
|||
Текст функции—обработчика кнопки Посчитать |
||||
//———————- |
||||
char ch; |
||||
String OutStr = Edit2->Text; |
||||
for (int i=1; i<Kol; i++) { |
||||
ch = StringGrid1->Cells[0][i][1]; |
||||
mas[int(ch)]=StrToFloat(StringGrid1->Cells[1][i]); |
||||
} |
||||
Edit3->Text=FloatToStr(Rezult(OutStr)); |
||||
//—————— |
Функция реализации приоритета операций ————————— |
|||
int Prior ( char a ){ |
||||
switch ( a ) { |
return 4; |
|||
case ‘^’: |
case ‘/’: |
|||
case ‘*’: |
return 3; |
|||
case ‘-‘: |
case ‘+’: |
return 2; |
||
case ‘(‘: |
return 1; |
|||
} |
||||
return 0; |
} |
|
//——————— |
Расчет арифметического выражения ——————————— |
double Rezult(String Str) { char ch, ch1, ch2; double op1, op2, rez;
znak << ‘*’ << ‘/’ << ‘+’ << ‘-‘ << ‘^’; char chr = ‘z’+1;
for (int i=1; i <= Str.Length(); i++){ ch=Str[i];
if (! znak.Contains(ch)) begin = InStack(begin, ch);
27
else { |
||
begin = OutStack(begin,&ch1); |
||
begin = OutStack(begin,&ch2); |
||
op1 = mas[int (ch1)]; |
||
op2 = mas[int (ch2)]; |
||
switch (ch){ |
rez=op2+op1; |
break; |
case ‘+’ : |
||
case ‘-‘ : |
rez=op2-op1; |
break; |
case ‘*’ : |
rez=op2*op1; |
break; |
case ‘/’ : |
rez=op2/op1; |
break; |
case ‘^’ : |
rez=pow(op2,op1); |
break; |
} |
||
mas[int (chr)] = rez; |
begin = InStack(begin,chr); chr++;
}
}
return rez;
}
4.3. Индивидуальные задания
Написать программу формирования ОПЗ и расчета полученного выраже— ния. Разработать удобный интерфейс ввода исходных данных и вывода резуль— татов. Работу программы проверить на конкретном примере (табл. 4.1).
Таблица 4.1 |
|||||||
Выражение |
a |
b |
c |
d |
e |
Результат |
|
1. |
a/(b–c)*(d+e) |
8.6 |
2.4 |
5.1 |
0.3 |
7.9 |
– 26.12 |
2. |
(a+b)*(c–d)/e |
7.4 |
3.6 |
2.8 |
9.5 |
0.9 |
– 81.89 |
3. |
a–(b+c*d)/e |
3.1 |
5.4 |
0.2 |
9.6 |
7.8 |
2.16 |
4. |
a/b–((c+d)*e) |
1.2 |
0.7 |
9.3 |
6.5 |
8.4 |
– 131.006 |
5. |
a*(b–c+d)/e |
9.7 |
8.2 |
3.6 |
4.1 |
0.5 |
168.78 |
6. |
(a+b)*(c–d)/e |
0.8 |
4.1 |
7.9 |
6.2 |
3.5 |
2.38 |
7. |
a*(b–c)/(d+e) |
1.6 |
4.9 |
5.7 |
0.8 |
2.3 |
– 0.413 |
8. |
a/(b*(c+d))–e |
8.5 |
0.3 |
2.4 |
7.9 |
1.6 |
1.151 |
9. |
(a+(b/c–d))*e |
5.6 |
7.4 |
8.9 |
3.1 |
0.2 |
0.666 |
10. a*(b+c)/(d–e) |
0.4 |
2.3 |
6.7 |
5.8 |
9.1 |
– 1.091 |
|
11. a–(b/c*(d+e)) |
5.6 |
3.2 |
0.9 |
1.7 |
4.8 |
– 17.51 |
|
12. (a–b)/(c+d)*e |
0.3 |
6.7 |
8.4 |
9.5 |
1.2 |
– 0.429 |
|
13. |
a/(b+c– d*e) |
7.6 |
4.8 |
3.5 |
9.1 |
0.2 |
1.173 |
14. |
a*(b– c)/(d+e) |
0.5 |
6.1 |
8.9 |
2.4 |
7.3 |
– 0.144 |
15. |
(a+b*c)/(d– e) |
9.1 |
0.6 |
2.4 |
3.7 |
8.5 |
– 2.196 |
16. |
a– b/(c*(d – e)) |
1.4 |
9.5 |
0.8 |
6.3 |
7.2 |
14.594 |
28
Примеры реализации[править]
C++[править]
//Стандарт языка 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; }
Forth[править]
Haskell[править]
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
Erlang[править]
-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.
C[править]
/* Компиляция: * 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.3fn", stack[i]); return(0); } int parse(char *s) { double tval = 0; char * endptr; /* Распознаем знаки арифметических операций */ switch (*s) { case '-': /* Если минус является первым и не последним символом аргумента, * значит пользователь ввел отрицательное число и опознавать его * как операцию вычитания не нужно */ if (*(s+1) != '') 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 != '') return(UNK); /* Проверяем, есть ли свободное место в стеке * и сохраняем в нем операнд, иначе возвращаем ошибку переполнения */ if (scount < STKDPTH) stack[scount++] = tval; else return(SOF); return(VAL); }
Delphi[править]
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) КОН КОН Перевести; КОН ОПН.
PHP[править]
Пример использования класса 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]; } }
C#[править]
Функция 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()); } } }
Ruby[править]
# Преобразование к польской записи # <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
Javascript[править]
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(); };
Python[править]
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()
1.
Обратная польская запись (ОПЗ)
Сложные вычислительные задачи обычно требуют больших объемов вычислений, поэтому к
разработчикам языков программирования
предъявлялось требование: максимально приблизить форму записи математических выражений в коде программы к естественному
языку математики.
Одну из первых областей системного программирования составили исследования способов
трансляции математических выражений.
2.
В результате наибольшее распространение получил метод трансляции с помощью обратной
польской
записи,
которую
предложил
польский математик Я. Лукашевич.
ОПЗ представляет собой выражение, записанное
в постфиксной форме, без скобок, по
специальным правилам.
3.
Пусть для операндов А и В выполняется
операция сложения.
Привычная форма записи А+В называется
инфиксной.
Форма записи, в которой знак операции следует
перед
операндами
+АВ,
называется
префиксной.
Если же операция записывается после операндов
АВ+, то это постфиксная форма.
Получение ОПЗ реализуется с использованием
структур в виде стека и дерева.
4.
Алгоритм, использующий стек
Получение ОПЗ с использованием стека может
осуществляться весьма просто на основе
алгоритма, предложенного Дейкстрой, который ввел понятие стекового приоритета
операций, например:
Операция
(
Приоритет
1
+ –
* /
2
3
5.
Суть алгоритма в следующем
Исходное выражение, записанное в виде строки
символов S, просматривается слева направо.
Операнды переписываются в выходную строку
В, операции обрабатываются с использованием
стека, который первоначально пуст, на основе
следующих правил.
1. Если в строке S встретился операнд, то его
помещаем в строку В.
2. Если в S встретилась открывающая скобка,
то ее помещаем в стек.
6.
3. Если в S встретилась закрывающая скобка, то
извлекаем из стека и записываем в строку В все
операции до «(«, саму «(» скобку также
извлекаем из стека; обе скобки игнорируются.
4. Если в S встретилась операция Х, то выталкиваем из стека все операции, приоритет которых не ниже Х, после чего саму операцию Х
записываем в стек.
5. При достижении конца строки S, анализируем
стек и, если он не пуст, извлекаем и переписываем его элементы в выходную строку В.
7.
Пример реализации
Исходное выражение задано в виде строки S
«a + b*c + ( d*e + f )*g»
Запишем это выражение в форме ОПЗ.
Ответом будет выражение (без скобок)
abc*+de*f+g*+
Результат будем получать в строке В.
Начинаем последовательно просматривать символы исходной строки, причем В – пустая
строка и стек пуст.
8.
Всего в строке 15 символов (15 п.п.).
1. Букву «a» помещается в строку В
2. Операцию «+» помещаем в стек.
3. Букву «b» помещаем в строку В.
На этот момент стек и строка В выглядят
следующим образом:
В = ” ab ”
+
9.
4. Операцию «*» помещаем в стек, т.к. элемент
«+» в вершине стека имеет более низкий
приоритет.
5. Букву «с» помещаем в строку В, после чего
имеем
В = ” abс ”
*
+
10.
6. Следующая операция «+»: анализируем стек и
видим, что в вершине стека «*» и следующая за
ней «+» имеют приоритеты не ниже текущей.
Следовательно, обе операции извлекаем из
стека и помещаем в строку В, а текущую
операцию «+» помещаем в стек.
В итоге имеем
В = ” abс*+ ”
+
11.
7. Далее следует символ «(», его помещаем в
стек.
8. Букву «d» помещаем в строку В.
В результате получается
В = ” abс*+d ”
(
+
12.
9.
Операцию «*» помещаем в стек,
приоритет у скобки самый низкий.
10. Букву «e» помещаем в строку В.
Получили
В = ” abс*+de ”
*
(
+
т.к.
13.
11. Следующая операция «+»: приоритет
операции «*» в вершине стека выше, поэтому
извлекаем из стека «*» и помещаем в строку В.
Текущий символ «+» помещаем в стек.
12. Букву «f» помещаем в строку В. Получаем
В = ” abс*+de*f ”
+
(
+
14.
13. Далее идет закрывающая скобка, все
элементы до символа «(» извлекаем из стека и
помещаем в строку В (это элемент «+»), сам
символ «(» тоже извлекаем из стека.
Обе скобки игнорируются:
В = ” abс*+de*f+ ”
+
15.
14. Операцию «*» помещаем в стек, т.к. ее
приоритет выше операции «+» в вершине
стека.
15. Букву «g» записываем в строку В.
Получаем
В = ” abс*+de*f+g ”
*
+
16.
Все символы строки S просмотрены, следовательно, анализируем состояние стека, если он
не пуст, то переписываем все его элементы в
строку В, т.е. операции «+» и «*» последовательно извлекаем из стека в строку:
В = ”abс*+de*f+g*+”
Просмотрев исходную информацию только один
раз, мы решили поставленную задачу.
17.
Вычисление выражения, записанного в ОПЗ,
может проводиться путем однократного
просмотра, что является весьма удобным при
генерации объектного кода программ.
Рассмотрим простой пример.
18.
Выражение (A + B) * (C + D) – E в виде ОПЗ:
AB+CD+*E–
Его вычисление проводится следующим образом
(R1 и R2 – вспомогательные переменные):
Шаг
1
2
3
4
5
Анализируемая строка
AB+CD+*E–
R1 C D + * E –
R1 R2 * E –
R1 E –
R1
Действие
R1 = A + B
R2 = C + D
R1 = R1 * R2
R1 = R1 – E
19.
Текст программы, реализующий рассмотренный
алгоритм, может иметь следующий вид:
…
struct Stack {
char c;
// Символ операции
Stack *next;
};
int Prior (char);
Stack* InS ( Stack*, char);
Stack* OutS ( Stack*, char*);
20.
void main ()
{
Stack *t,
*Op = NULL;
// Стек операций Op – пуст
char a, In [81], Out [81];
// In – входная (S), Out – выходная (B) строки
int k = 0, l = 0;
// Текущие индексы для строк
cout << » Input formula : «;
cin >> In;
21.
// Анализируем символы строки In
while ( In[k] != ») {
// 1. Если символ – буква, заносим ее в Out
if ( In[k] >= ‘a’ && In[k] <= ‘z’ )
Out[l++] = In[k];
// 2. Если «(», записываем ее в стек
if ( In[k] == ‘(‘ )
Op = InS ( Op, In[k] );
22.
/* 3. Если «)», извлекаем из стека в строку Out
все операции до открывающей скобки */
if ( In[k] == ‘)’ ) {
while ( (Op -> c) != ‘(‘ ) {
// Считываем элемент a из стека
Op = OutS ( Op, &a );
if ( !Op ) a = »;
// и записываем его в строку Out.
Out[l++] = a;
}
23.
// Удаляем из стека открывающую скобку
t = Op;
Op = Op -> next;
delete t;
}
24.
/* 4. Если операция, извлекаем из стека в Out операции с большим или равным приоритетом */
if (In[k]==’+’ || In[k]==’–’ || In[k]==’*’ || In[k]==’/’) {
while ( Op != NULL &&
Prior (Op -> c) >= Prior (In[k])) {
Op = OutS (Op, &a);
Out[l++] = a;
}
// Текущий символ операции – в стек
Op = InS (Op, In[k]);
}
k++;
}
// Конец цикла анализа входной строки
25.
/* 5. Если стек не пуст, извлекаем и записываем
операции в выходную строку */
while ( Op != NULL) {
Op = OutS (Op, &a);
Out[l++] = a;
}
Out[l] = ‘’;
// Окончание строки
cout << «n Polish = » << Out << endl;
getch();
}
// system(“pause”);
26.
//—— Реализация приоритета операций ——int Prior ( char a ) {
switch ( a ) {
case ‘*’:
case ‘/’:
return 3;
case ‘–’:
case ‘+’:
return 2;
case ‘(‘: return 1;
}
return 0;
}
27.
// ——— Добавление элемента в стек ——-Stack* InS ( Stack *p, char s)
{
Stack *t = new Stack;
t -> c = s;
t-> next = p;
return t;
}
28.
// ——- Извлечение элемента из стека ——Stack* OutS ( Stack *p, char *s )
{
Stack *t = p;
*s = p -> c;
p = p -> next;
delete t;
return p;
}
29.
Пример, реализованный
оконного приложения:
в
методичке
для
30.
struct Stack {
char info;
Stack *next;
} *begin;
int Prior (char);
Stack* InStack ( Stack*, char);
Stack* OutStack ( Stack*, char*);
double Rezult (String);
double mas[100];
// Массив для вычисления
Set < char, 0, 255 > znak;
// Множество символов-знаков
int Kol = 10;
31.
//—- Текст функции-обработчика FormCreate —Edit1->Text = «a+b*(c-d)/e»;
Edit2->Text = «»;
char a = ‘a’;
StringGrid1->Cells[0][0] =»Имя»;
StringGrid1->Cells[1][0] =»Знач.»;
for (int i = 1; i <= Kol; i++) {
StringGrid1->Cells[0][i] = a++;
StringGrid1->Cells[1][i] = i;
}
32.
// —- Текст обработчика кнопки Перевести —Stack *t;
begin = NULL;
char ss, a;
String InStr, OutStr;
OutStr = «»;
Edit2->Text = «»;
InStr = Edit1->Text;
znak << ‘*’ << ‘/’ << ‘+’ << ‘-‘ << ‘^’;
int len = InStr.Length(), k;
33.
for (k = 1; k <= len; k++) {
ss = InStr[k];
// ———— Пункт 1 алгоритма ————if (ss >= ‘a’ && ss <= ‘z’ )
OutStr += ss;
// ———— Пункт 2 алгоритма ————if ( ss == ‘(‘ )
begin = InStack ( begin, ss);
34.
// ———— Пункт 3 алгоритма ————if ( ss == ‘)’ ) {
while ( (begin -> info) != ‘(‘ ) {
begin = OutStack ( begin, &a );
OutStr += a;
}
begin = OutStack ( begin, &a );
}
35.
// ———— Пункт 4 алгоритма ————if ( znak.Contains ( ss ) ) {
while ( begin != NULL &&
Prior ( begin->info ) >= Prior ( ss ) ) {
begin = OutStack ( begin, &a );
OutStr += a;
}
begin = InStack ( begin, ss );
}
}
// Конец оператора if
// Конец оператора for
36.
// ———— Пункт 5 алгоритма ————while ( begin != NULL) {
begin = OutStack ( begin, &a );
OutStr += a;
}
Edit2 -> Text = OutStr;
// Выводим полученную строку
}
37.
//—- Текст обработчика кнопки Посчитать —char ch;
String OutStr = Edit2 -> Text;
for ( int i = 1; i <= Kol; i++) {
ch = StringGrid1 -> Cells[0][i][1];
mas[int(ch)] = StrToFloat(SG1->Cells[1][i]);
}
// SG это StringGrid
Edit3->Text=FloatToStr( Rezult ( OutStr ) );
38.
//— Функция реализации приоритета операций -int Prior ( char a ){
switch ( a ) {
case ‘^’:
case ‘*’:
case ‘/’:
case ‘-‘:
case ‘+’:
case ‘(‘:
}
return 0;
}
return 4;
return 3;
return 2;
return 1;
// !!!
39.
//—— Расчет арифметического выражения ——double Rezult(String Str)
{
char ch, ch1, ch2, chr;
double op1, op2, rez;
int len = Str.Length();
znak << ‘*’ << ‘/’ << ‘+’ << ‘-‘ << ‘^’;
chr = ‘z’ + 1;
for (int i=1; i <= len; i++) {
ch = Str[i];
40.
if (! znak.Contains (ch) )
begin = InStack ( begin, ch );
else {
begin = OutStack ( begin, &ch1 );
begin = OutStack ( begin, &ch2 );
op1 = mas[int (ch1) — 97]; // код ‘a’ — 97
op2 = mas[int (ch2) — 97];
41.
switch (ch){
case ‘+’ : rez = op2 + op1;
case ‘-‘ : rez = op2 — op1;
case ‘*’ : rez = op2 * op1;
case ‘/’ : rez = op2 / op1;
case ‘^’ : rez = pow(op2,op1);
}
mas[int (chr) — 97] = rez;
begin = InStack ( begin, chr);
chr++;
}
// Конец else
}
// Конец оператора for
return rez;
}
break;
break;
break;
break;
break;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
uses math; type arr = array of string[20]; //процедура помещения токена в стек procedure push(var x: arr; var y: string); begin setlength(x, length(x) + 1); //увеличиваем глубину стека x[high(x)] := y //помещаем токен end; //процедура извлечения токена из стека procedure pop(var x: arr; var y: string); begin y := x[high(x)]; //извлекаем токен setlength(x, length(x) - 1) //уменьшаем глубину стека end; const op = '()+-*/^'; //операторы в порядке возрастания приоритетов var s, t, q, error: string; //входная строка, буферы токена, сообщение об ошибке i, err: integer; //счётчик, ошибка преобразования строки в число num: real; //проверочный результат st, st_op: arr; //выходной стек, стек операций st_n: array of real; //стек операндов begin writeln('Введите выражение:'); //ввод выражения в инфиксной нотации readln(s); s := s + ' '; //добавляем пробел для удобства алгоритма for i := length(s) downto 1 do //разбиваем пробелами выражение на токены if pos(s[i], op) > 0 //если очередной символ - оператор then begin //то вставляем пробелы до и после него insert(' ', s, i + 1); insert(' ', s, i) end; while pos(' ', s) > 0 do delete(s, pos(' ', s), 1); //убираем лишние пробелы if (length(s) > 0) and (s[1] = ' ') then delete(s, 1, 1); if length(s) = 0 //если строка пустая then error := 'Ошибка: пустое выражение' //формируем сообщение об ошибке и более ничего не делаем else begin //если строка не пустая, то writeln('Выражение:'); //печатаем выражение, разбитое на токены writeln(s); t := ''; //инициализируем буфер токена: пока пустой for i := 1 to length(s) do //сканируем выражение begin if s[i] <> ' ' //если не пробел, then t := t + s[i] //то помещаем символ в буфер токена else begin //иначе разбираемся с токеном case t[1] of //смотрим первый символ токена '0'..'9', '.': //если относится к числу, begin //то val(t, num, err); //проверяем, является ли токен допустимой записью числа if err = 0 then push(st, t) //если да, то помещаем токен в выходной стек else begin //если нет, error := 'Ошибка: не число: ' + t; //то формируем текст ошибки break //и прекращаем сканирование end end; '(': //если открывающая скобка, push(st_op, t); //то помещаем токен в стек ')': begin //если закрывающая скобка, while (length(st_op) > 0) and (t[1] <> '(') do //то пока не встретился токен '(' и стек операторов не опустел begin pop(st_op, t); //извлекаем токен из стека операторов if t[1] <> '(' then push(st, t) //и помещаем его в выходной стек end; if t[1] <> '(' //если последний извлечённый токен не '(', then begin error := 'Ошибка: нарушен баланс скобок'; //то формируем текст ошибки break //и прекращаем сканирование end end; '+', '-', '*', '/', '^': //если токен - операция, begin //то пока стек операторов не пустой и операция на вершине стека операторов с большим или равным приоритетом, while (length(st_op) > 0) and ((pos(t[1], op) - 1) div 2 <= (pos(st_op[high(st_op)][1], op) - 1) div 2) do begin pop(st_op, q); //извлекаем токен из стека операторов push(st, q); //помещаем токен в выходной стек end; push(st_op, t); //добавляем токен к стеку операторов end else //если токен какой-то другой, begin error := 'Ошибка: недействительный оператор: ' + t; //то формируем текст ошибки break //и прекращаем сканирование end end; t := '' //реинициализируем буфер токена end; end; //сканирование окончено while length(st_op) > 0 do //если в стеке операторов остались операторы, перемещаем их в выходной стек if pos(st_op[high(st_op)][1], op) < 3 //если оператор - скобка, then begin error := 'Ошибка: нарушен баланс скобок'; //то формируем текст ошибки break //и прекращаем перемещение end else begin //если оператор - не скобка, pop(st_op, t); //то извлекаем его из стека операторов push(st, t) //и помещаем в выходной стек end; end; if (length(st) > 0) and (length(error) = 0) //если выходной стек не пуст и нет ошибки, then begin //то writeln('ОПЗ:'); //печатаем выражение в постфиксной нотации for i := 0 to high(st) do write(st[i], ' '); writeln; for i := 0 to high(st) do //пробегаемся по выходному стеку и пытаемся вычислить выражение case st[i][1] of '+', '-', '*', '/', '^': //если токен - оператор, begin //то пытаемся выполнить операцию case st[i][1] of //смотрим, что за оператор '+': st_n[high(st_n) - 1] := st_n[high(st_n) - 1] + st_n[high(st_n)]; //находим сумму '-': st_n[high(st_n) - 1] := st_n[high(st_n) - 1] - st_n[high(st_n)]; //находим разность '*': st_n[high(st_n) - 1] := st_n[high(st_n) - 1] * st_n[high(st_n)]; //находим произведение '/': //деление if st_n[high(st_n)] <> 0 //если делитель не равен 0 then st_n[high(st_n) - 1] := st_n[high(st_n) - 1] / st_n[high(st_n)] //то находим частное else begin //иначе ошибка деления на 0 error := 'Ошибка: деление на ноль'; break end; '^': //возведение в степень if (st_n[high(st_n) - 1] = 0) and (st_n[high(st_n)] = 0) //если 0^0 then begin //то ошибка 0^0 error := 'Ошибка: 0^0'; break end else if (frac(1 / st_n[high(st_n)]) = 0) and not odd(trunc(1 / st_n[high(st_n)])) and (st_n[high(st_n) - 1] < 0) then begin //если корень чётной степени из отрицательного числа, то ошибка error := 'Ошибка: корень чётной степени из отрицательного числа'; break end else if st_n[high(st_n) - 1] > 0 //если всё в норме, вычисляем степень then st_n[high(st_n) - 1] := exp(ln(st_n[high(st_n) - 1]) * st_n[high(st_n)]) else if st_n[high(st_n) - 1] < 0 then st_n[high(st_n) - 1] := -exp(ln(-st_n[high(st_n) - 1]) * st_n[high(st_n)]) else st_n[high(st_n) - 1] := 0 end; setlength(st_n, length(st_n) - 1) //операция выполнена, убираем второй операнд из стека (результат помещён на место первого операнда) end else begin //если не оператор, setlength(st_n, length(st_n) + 1); //то увеличиваем глубину стека операндов val(st[i], st_n[high(st_n)], err) //и помещаем в стек операндов токен, преобразованный в число end end end; if length(error) > 0 //если есть ошибка, then write(error) //то печатаем её else write('Результат: ', st_n[high(st_n)]:0:15); //иначе печатаем результат readln end. |
Обратная польская запись
С помощью данного сервиса можно онлайн получить обратную польскую запись.
- Ввод данных
- Видеоинструкция
- Оформление Word
Инструкция
Для арифметических выражений
Все математические операции выражаются через общепринятые символы (+ , - , * , / , ^ ).
Для логических выражений
! Отрицание (НЕ)
| Штрих Шеффера (И-НЕ)
# Стрелка Пирса (ИЛИ-НЕ)
* Конъюнкция (И)
+ Дизъюнкция (ИЛИ)
^ Исключающее ИЛИ, сумма по модулю 2 (XOR)
@ Импликация (ЕСЛИ-ТО)
% Обратная импликация
= Эквивалентность (РАВНО)
Если в качестве исходных данных задана уже готовая обратная польская запись, то необходимо отметить этот пункт. В этом случае все символы разделяются пробелом.
Например, x y z ! * x + z * +
Алгоритм разбора выражений
- Предварительная обработка символьной строки (проверка на наличие ошибок, сокращение выражения).
- Пока есть ещё символы для чтения:
- Читаем очередной символ.
- Если символ является числом или постфиксной функцией (например, ! — факториал), добавляем его к выходной строке.
- Если символ является префиксной функцией (например, sin — синус), помещаем его в стек.
- Если символ является открывающей скобкой, помещаем его в стек.
- Если символ является закрывающей скобкой:
- До тех пор, пока верхним элементом стека не станет открывающая скобка, выталкиваем элементы из стека в выходную строку. При этом открывающая скобка удаляется из стека, но в выходную строку не добавляется.
- Если существуют разные виды скобок, появление непарной скобки также свидетельствует об ошибке. Если какие-то скобки одновременно являются функциями (например, [x] — целая часть), добавляем к выходной строке символ этой функции.
- Если символ является бинарной операцией о1, тогда:
- пока на вершине стека префиксная функция…
- ИЛИ операция на вершине стека приоритетнее o1;
- ИЛИ операция на вершине стека левоассоциативная с приоритетом как у o1;
- выталкиваем верхний элемент стека в выходную строку.
- помещаем операцию o1 в стек.
- пока на вершине стека префиксная функция…
- Когда все символы входной строки пере, выталкиваем все символы из стека в выходную строку.
Приоритеты:
- высокий: ^
- средний: * /
- низкий: + −
- самый низкий: ( )
Пример разбора арифметического выражения
sqrt(x)-1/2*sin(x^2-2)
Символ | Операция | Стек | Выходная строка |
sqrt | поместить в стек | s | |
( | поместить в стек | s, ( | |
x | добавить к выходной строке | s, ( | x |
) | 1) присоединить содержимое стека до скобки в обратном порядке к выходной строке; 2) удалить скобку из стека. | s | x |
— | 1) присоединить стек в обратном порядке к выходной строке; 2) поместить новую операцию в стек. | — | x, sqrt |
1 | добавить к выходной строке | — | x, sqrt, 1 |
/ | поместить в стек | -, / | x, sqrt, 1 |
2 | добавить к выходной строке | -, / | x, sqrt, 1, 2 |
* | 1) присоединить стек в обратном порядке к выходной строке; 2) поместить новую операцию в стек. | -, * | x, sqrt, 1, 2, / |
sin | поместить в стек | -, *, s | x, sqrt, 1, 2, / |
( | поместить в стек | -, *, s, ( | x, sqrt, 1, 2, / |
x | добавить к выходной строке | -, *, s, ( | x, sqrt, 1, 2, /, x |
^ | поместить в стек | -, *, s, (, ^ | x, sqrt, 1, 2, /, x |
2 | добавить к выходной строке | -, *, s, (, ^ | x, sqrt, 1, 2, /, x, 2 |
— | 1) присоединить стек в обратном порядке к выходной строке; 2) поместить новую операцию в стек. | -, *, s, (, — | x, sqrt, 1, 2, /, x, 2, ^ |
2 | добавить к выходной строке | -, *, s, (, — | x, sqrt, 1, 2, /, x, 2, ^, 2 |
) | 1) присоединить содержимое стека до скобки в обратном порядке к выходной строке; 2) удалить скобку из стека. | -, *, s | x, sqrt, 1, 2, /, x, 2, ^, 2, — |
Присоединяем стек в обратном порядке к выходной строке: x sqrt 1 2 / x 2 ^ 2 — sin * —
Пример разбора логического выражения
x+(y*!z+x)*z
Символ | Операция | Стек | Выходная строка |
x | добавить к выходной строке | x | |
+ | поместить в стек | + | x |
( | поместить в стек | +, ( | x |
y | добавить к выходной строке | +, ( | x, y |
* | поместить в стек | +, (, * | x, y |
! | поместить в стек | +, (, *, ! | x, y |
z | добавить к выходной строке | +, (, *, ! | x, y, z |
+ | 1) присоединить стек в обратном порядке к выходной строке; 2) поместить новую операцию в стек. | +, (, + | x, y, z, !, * |
x | добавить к выходной строке | +, (, + | x, y, z, !, *, x |
) | 1) присоединить содержимое стека до скобки в обратном порядке к выходной строке; 2) удалить скобку из стека. | + | x, y, z, !, *, x, + |
* | поместить в стек | +, * | x, y, z, !, *, x, + |
z | добавить к выходной строке | +, * | x, y, z, !, *, x, +, z |
Присоединяем стек в обратном порядке к выходной строке: x y z ! * x + z * +
Список литературы
- Обратная польская запись
Стоимость подключения зависит от срока использования:
- 1 месяц: 100 руб.
- 3 месяца: 200 руб.
- 6 месяцев: 300 руб.
- 1 год: 600 руб.
Возможности:
- Скачивать решение в формате Word (форматы rtf, docx, xlsx).
- Использовать калькуляторы без рекламы.
Оплата осуществляется в Личном кабинете в разделе Платные услуги
.