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

Урок 22, Задача 8

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

5*(3+4)-7*9+3*(2+(2-7))
(арифметическое выражение со скобками любого уровня вложенности и операциями умножения,
вычитания и сложения)

и в качестве ответа возвращает результат этого выражения.

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

Функция raskr_skob находит выражение, заключённое в скобки, копирует его в подстроку, отправляя затем в процедуру, которая раскладывает её на числа и знаки, формируя два массива. Эти массивы отправляются в функцию, которая считает результат выражения и переводит этот результат в строковый тип, возвращая его. Функция raskr_skob вставляет это значение вместо того, что было в скобках. Из-за множественной вложенности после цикла остаётся вероятность, что не все скобки раскрыты.
Тогда функция вызывает саму себя. Когда скобок в выражении не остаётся, клиентский код отправляет оставшуюся строку на вычисление уже без рекурсии

type
  intArr = array[1..100] of integer;
  chArr = array[1..99] of char;
var
  a : intarr;
  z : chArr;
  st: string;
  ai, zi: integer; //предельные индексы получившихся массивов
  procedure getData(subst: string; var a : intarr; var z : chArr; var ai, zi: integer);
  var
     s : string;
     j: integer;
  begin
    s := '';
    zi := 0;
    ai := 0;

    for j := low(a) to high(a) do
      a[j] := 0;

    for j := low(z) to high(z) do
      z[j] := #0;

    j := 1;
    while j <= length(subst) do
    begin
      while (subst[j] >= '0') and (subst[j] <= '9') do
      begin
        s := s + subst[j];
        j += 1;
      end;

      if (s <> '') then
      begin
        ai += 1;
        val(s, a[ai]);
        s := '';
      end;

      if j > length(subst) then break;

      if (subst[j] < '0') or (subst[j] > '9') then
      begin
        zi += 1;
        z[zi] := subst[j];
        j += 1;
        if (subst[j] < '0') or (subst[j] > '9') then
        begin                //если следом отрицательное число. Чтобы минус попал
                             // в числа, а не в символы. Напр: "5+-2"
          s := s + subst[j];
          j += 1;
        end;

      end;


    end;
  end;

  function calculate(a : intarr; z : chArr; ai, zi: integer): string;
  var
     i , j, res : integer;

  begin
    i := 1;
    while i <= zi do
    begin
      if z[i] = '*' then
      begin
        a[i] := a[i] * a[i+1];
        for j := (i + 1) to ai-1 do
          a[j] := a[j+1];

        a[ai] := 0;
        for j := i to zi-1 do
          z[j] := z[j+1];

        z[zi] := #0;
      end
      else
        i += 1;
    end;

    res := a[1];
    for i := 1 to zi do
      if z[i] = '-' then
        res := res - a[i+1]
      else
        res := res + a[i+1];

    str(res, result);
  end;
function raskr_skob(var st: string ): string;
var subst: string;
   i, ind, count: integer;
   open_skob: boolean;
begin
  for i := 1 to length(st) do
  begin
    if st[i] = '(' then
    begin
      ind := i;
      count := 1;
      open_skob := true;
    end;

    if (st[i] = ')') and open_skob then
    begin
      subst := Copy (st, ind+1, count-2);  //чтобы копировать строку без скобок
      Delete (st, ind, count);
      getData(subst, a, z, ai, zi);
      subst := calculate(a, z, ai, zi);
      Insert (subst, st, ind);
      open_skob := false;
    end;
    count += 1;
  end;
  for i := 1 to length(st) do
    if st[i] = '(' then
      raskr_skob(st);
end;


begin
  st := '5*(3+(2+(3-1)))-7*9+3*(2+(2-7))';
  write(st);
  raskr_skob(st);
  getData(st, a, z, ai, zi);
  writeln('= ', calculate(a, z, ai, zi));

  readln();
end. 
vedro-compota's picture

1) Решаем без массивов


type
  intArr = array[1..100] of integer;
  chArr = array[1..99] of char;

2) Основная функция должна быть быть рекурсивной, принимать на выход строку и возвращать число (ответ на выражение) -- это же относится к предыдущей задаче (вычисление "плоского" выражения без скобок - лучше начать с нее и опубликовать).

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

var
  s: string;
  i: integer;
function getSumm(a, b: integer): integer;
begin
  result := b + a;
end;

function getMult(var a: integer; b: integer; var mult: boolean): integer;
begin
  result := a * b;
  mult := false;
end;

function TakeResult(var i: integer; s: string): integer;
var
  str: string;
  a, midres: integer;
  minus, mult: boolean;
begin
  minus := false;
  mult := false;
  result := 0;
  repeat
    i += 1;
    if (s[i] >= '0') and (s[i] <= '9') then
      str := str + s[i]
    else
    begin
      if s[i] = '(' then
      begin
        a := TakeResult(i, s);
        i += 1;
      end
      else
        val(str, a);

      if minus = true then
      begin
        a := a*(-1);
        minus := false;
      end;

      if mult = true then
        a := getMult(a, midres, mult);

      if s[i] = '*' then
      begin
        mult := true;
        midres := a;
      end
        else
          result := getSumm(a, result);

      if s[i] = '-' then
        minus := true;
      str := '';
    end;
  until (s[i] = ')') or (i > length(s));
end;

begin
i := 0;
s := '5*(3+(2*2-(2+2)))+7*9+3*(2+(2-7))+5';
writeln(s,' = ', TakeResult(i, s));
readln();
end. 

var
  s: string;
  i: integer;
function TakeResult(var i: integer; s: string): integer;
var
  str: string;
  a, midres: integer;
  minus, mult: boolean;
begin
  minus := false;
  mult := false;
  result := 0;
  repeat
    i += 1;
    if (s[i] >= '0') and (s[i] <= '9') then
      str := str + s[i]
    else
    begin
      if s[i] = '(' then
      begin
        a := TakeResult(i, s);
        i += 1;
      end
      else
        val(str, a);

      if minus = true then
      begin
        a := a*(-1);
        minus := false;
      end;

      if mult then
      begin
        a := a * midres;
        mult := false;
      end;

      if s[i] = '*' then
      begin
        mult := true;
        midres := a;
      end
        else
          result := result + a;

      if s[i] = '-' then
        minus := true;
      str := '';
    end;
  until (s[i] = ')') or (i > length(s));
end;

begin
i := 0;
s := '5*(3+(2*2-(2+2)))+7*9+3*(2+(2-7))+5';
writeln(s,' = ', TakeResult(i, s));
readln();
end.