Как составить опз

Цель работы: изучить правила формирования постфиксной записи арифметических выражений с использованием стека.

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/(bc)*(d+e)

8.6

2.4

5.1

0.3

7.9

– 26.12

2.

(a+b)*(cd)/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*(bc+d)/e

9.7

8.2

3.6

4.1

0.5

168.78

6.

(a+b)*(cd)/e

0.8

4.1

7.9

6.2

3.5

2.38

7.

a*(bc)/(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/cd))*e

5.6

7.4

8.9

3.1

0.2

0.666

10. a*(b+c)/(de)

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. (ab)/(c+d)*e

0.3

6.7

8.4

9.5

1.2

– 0.429

13.

a/(b+cd*e)

7.6

4.8

3.5

9.1

0.2

1.173

14.

a*(bc)/(d+e)

0.5

6.1

8.9

2.4

7.3

– 0.144

15.

(a+b*c)/(de)

9.1

0.6

2.4

3.7

8.5

– 2.196

16.

ab/(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 * +

Алгоритм разбора выражений

  1. Предварительная обработка символьной строки (проверка на наличие ошибок, сокращение выражения).
  2. Пока есть ещё символы для чтения:
    • Читаем очередной символ.
    • Если символ является числом или постфиксной функцией (например, ! — факториал), добавляем его к выходной строке.
    • Если символ является префиксной функцией (например, sin — синус), помещаем его в стек.
    • Если символ является открывающей скобкой, помещаем его в стек.
    • Если символ является закрывающей скобкой:
      • До тех пор, пока верхним элементом стека не станет открывающая скобка, выталкиваем элементы из стека в выходную строку. При этом открывающая скобка удаляется из стека, но в выходную строку не добавляется.
      • Если существуют разные виды скобок, появление непарной скобки также свидетельствует об ошибке. Если какие-то скобки одновременно являются функциями (например, [x] — целая часть), добавляем к выходной строке символ этой функции.
    • Если символ является бинарной операцией о1, тогда:
      1. пока на вершине стека префиксная функция…
        • ИЛИ операция на вершине стека приоритетнее o1;
        • ИЛИ операция на вершине стека левоассоциативная с приоритетом как у o1;
        • выталкиваем верхний элемент стека в выходную строку.
      2. помещаем операцию o1 в стек.
  3. Когда все символы входной строки пере, выталкиваем все символы из стека в выходную строку.

Приоритеты:

  • высокий: ^
  • средний: * /
  • низкий: + −
  • самый низкий: ( )

Пример разбора арифметического выражения

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. Обратная польская запись

Стоимость подключения зависит от срока использования:

  • 1 месяц: 100 руб.
  • 3 месяца: 200 руб.
  • 6 месяцев: 300 руб.
  • 1 год: 600 руб.

Возможности:

  • Скачивать решение в формате Word (форматы rtf, docx, xlsx).
  • Использовать калькуляторы без рекламы.

Оплата осуществляется в Личном кабинете в разделе Платные услуги.

Понравилась статья? Поделить с друзьями:

Не пропустите также:

  • График производной как найти точки экстремума
  • Как найти панель задач в интернете
  • Как найти начало обмоток в трехфазном электродвигателе
  • Как найти по номеру телефона предприятие
  • Ошибка 1075 windows 7 как исправить

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии