Урок 21 Задача 8

Урок 21 Задача 8:

"Калькулятор":
Напишите функцию, которая получает на вход произвольную строку вида:

5*(3+4)-7*9+3*(2+(2-7))

(арифметическое выражение со скобками любого уровня вложенности и операциями умножения, вычитания и сложения)
и в качестве ответа возвращает результат этого выражения.
Рекомендация: сначала убедитесь, что число открывающих скобок, равно числу закрывающих.

type
  massivStrok = array[1..1000] of string;
var
  s: string;
  stroki: massivStrok;

{функция получает на вход массив строк,
смещает пустые элементы массива влево (за один проход),
возвращает смещенный массив}
function sdvig(stroki: massivStrok; i: integer): massivStrok;
var
  x: integer;
begin
  x:= 1;
  while not(stroki[i+1] = 'N') do
  begin
    if stroki[i] = '' then
    begin
      if stroki[i-x] = '' then
      begin
        x:= x + 1
      end else begin
        stroki[i]:= stroki[i-x];
        stroki[i-x]:= '';
      end;
    end;
    if not (stroki[i] = '') then
      i:= i-1;
  end;
  result:= stroki;
end;

{функция получает на вход цифру,
возвращает запись цифры в строковом виде}
function preobr (a: integer): string;
var
  s: string;
begin
    if a = 1 then
      s:='1'
    else if a = 2 then
        s:='2'
      else if a = 3 then
          s:='3'
        else if a = 4 then
            s:='4'
          else if a = 5 then
              s:='5'
            else if a = 6 then
                s:='6'
              else if a = 7 then
                  s:='7'
                else if a = 8 then
                    s:='8'
                  else if a = 9 then
                      s:='9'
                    else
                      s:='0';
  result:= s;
end;

{функция получает на вход число,
возвращает запись числа в строковом виде}
function chislo (a: integer): string;
var
  b, d, x, h, i: integer;
  n: string;
begin
  if a < 0 then
  begin
    n:= n + '-';
    a:= -1*a;
  end;
  b:= a;
  d:=1;
  h:=1;
  while not (b div 10 = 0) do
  begin
    b:= b div 10;
    d:= d*10;
    h:= h+1;
  end;
  for i:=1 to h do
  begin
    x:= a div d;
    a:= a mod d;
    d:= d div 10;
    n:= n + preobr(x);
  end;
  result:= n;
end;

{функция выполняет арифметические действия в границах скобок
либо в границах всего выражения, если скобок нет}
function dejstvie(stroki: massivStrok; i: integer): massivStrok;
var
  a, b, m: integer;
  q: boolean;
begin
  q:= true;
  m:= i;
  while not ((stroki[m-2] = '(') or (stroki[m-2] = 'N')) do
  begin
    if (stroki[i] = '(') or (stroki[i] = 'N') then
      q:= false;
    if (stroki[i] = '*') or ((stroki[i] = '+') or (stroki[i] = '-')) and (q=false) then
    begin
      val(stroki[i-1], a);
      val(stroki[i+1], b);
      if stroki[i] = '*' then
        a:= a*b
      else if stroki[i] = '+' then
          a:= a+b
        else if stroki[i] = '-' then
            a:= a-b;
      stroki[i]:= chislo(a);
      stroki[i-1]:= '';
      stroki[i+1]:= '';
      stroki:= sdvig(stroki, i+1);
    end else
      if q = false then
        i:= i+1
      else
        i:= i-1;
  end;
  if (stroki[m-2]='(') and (stroki[m]=')') then
  begin
    stroki[m-2] := '';
    stroki[m] := '';
    stroki:= sdvig(stroki, m);
  end;
  result:= stroki;
end;

{рекурсия}
function skobki(stroki: massivStrok; i: integer): massivStrok;
begin
  if (stroki[i] = 'K') then
    result := dejstvie(stroki, i)
  else
    if stroki[i] = ')' then
      result := skobki(dejstvie(stroki, i), i)
    else
      result:= skobki(stroki, i+1);
end;

{функция перезаписывает строку в массив строк,
где каждый элемент - это либо число,
либо знак действия,
либо скобка}
function perezapis(s: string): integer;
var
  i, j: integer;
  stroki: massivStrok;
begin
  i:= 0;
  j:= 1;
  stroki[1]:= 'N';
  while (i < length(s)) do
  begin
    i:= i+1;
    if (s[i]>='0')and(s[i]<='9')
        or (s[i]='*') or (s[i]='+') or (s[i]='-')
        or (s[i]='(') or (s[i]=')') then
    begin
      j:= j+1;
      if (s[i]>='0')and(s[i]<='9') then
      begin
        for i:= i to length(s) do
        begin
          if (stroki[j-1] = '-') and ((j = 3) or (stroki[j-2] = '(')) then
            j:= j-1;
          stroki[j]:= stroki[j] + s[i];
          if not ((s[i+1]>='0')and(s[i+1]<='9')) then
            break;
        end;
      end else
        stroki[j]:= s[i];
      if (j>4) and (stroki[j-3]='(') and (stroki[j-2]='-')and (stroki[j]=')') then
      begin
        stroki[j-3]:='-' + stroki[j-1];
        stroki[j-2]:='';
        stroki[j-1]:='';
        stroki[j]:='';
        j:= j-3;
      end;
    end;
  end;
  stroki[j+1]:= 'K';
  stroki:= skobki(stroki, 1);
  val(stroki[j], result);
end;

begin                 // тело основной программы
  s:= '5*(3-4)-7*(9-3)*(2*(2-7*(5-9)))';
  writeln(perezapis(s));
  readln();
end.
vedro-compota's picture

Добавить решение с использованием базовой функции, которая просто возвращает int

_____________
матфак вгу и остальная классика =)

var
  s: string;

function kalkulyator (s: string; i: integer): integer;
var
  n: string;
  a, b, x, j: integer;
  znak1, znak2: char;
  chislo1, chislo2, umnozhenie, skobki, k: boolean;
begin
  n:= '';
  znak1:= ' ';
  znak2:= ' ';
  chislo1:= false;
  chislo2:= false;
  umnozhenie:= false;
  skobki:= false;
  k:= false;
  j:= 0;
  while not k do   // цикл для обхода строки
  begin
    i:= i+1;
    if not skobki then
    begin
      if (s[i]>='0')and(s[i]<='9') or (s[i] = '(') then // если встретилось число или откр. скобка
      begin
        if (s[i]>='0')and(s[i]<='9') then
        begin
          for i:= i to length(s) do
          begin
            n:= n + s[i];
            if not ((s[i+1]>='0')and(s[i+1]<='9')) then
              break;
          end;
          val(n, x);
          n:= '';
        end else begin
          skobki:= true;
          x:= kalkulyator(s, i);   // рекурсивный вызов
        end;
        if umnozhenie then
        begin
          if chislo2 then
            b:= b*x
          else
            a:= a*x;
          umnozhenie:= false;
        end else begin
          if chislo1 then
          begin
            b:= x;
            chislo2:= true;
          end else begin
            a:= x;
            if znak1 = '-' then
              a:= 0-a;
            chislo1:= true;
          end;
        end;
      end else
        if (s[i] = '+') or (s[i] = '-') or (s[i] = '*')then // если встретился знак
          if (s[i] = '+') or (s[i] = '-') then  // если плюс или минус
          begin
            if chislo2 then
              znak2:= s[i]
            else
              znak1:= s[i];
          end else                              // если умножение
            umnozhenie:= true;
    end;
    if skobki then
    begin
      if s[i] = '(' then
        j:= j+1
      else
        if s[i] = ')' then
          j:= j-1;
    end;
    if (s[i] = ')') and (not skobki) or (i=length(s)) then
      k:= true
    else
      if  (j = 0) and (skobki) then
        skobki:= false;
    if chislo2 and (not (znak2 = ' ') or k) then
    begin
      if znak1 = '+' then
        a:= a+b
      else
        a:= a-b;
      znak1:= znak2;
      znak2 := ' ';
      chislo2 := false;
    end;
  end;
  result:= a;
end;

begin
  s:= '5*(3+4)-7*9+3*(2+(2-7))';
  // s:= '5-((-147+147)+7)*5';
  // s:= '-10*(3-(-4))';
  // s:= '-12-(-5)';
  // s:= '5*(3-4)-7*(9-3)*(2*(2-7*(5-9)))';
  // s:= '3*(-6)';
  // s:= '-(-450)';
  // s:= '-77';
  writeln (kalkulyator(s, 0));
  readln();
end.