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

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

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

(арифметическое выражение со скобками любого уровня вложенности и операциями умножения, вычитания и сложения)

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

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

Решение:

var
  s: string;
  i: integer;

 function arithmetic(num1,num2: integer; oper1: string): integer;
  begin
     case oper1 of
      '+': num1 := num1 + num2;
      '-': num1 := num1 - num2;
      '*': num1 := num1 * num2;
    end;
    result := num1;
  end; 

  function calc(var i: integer; s: string): integer;
  var Code,num2,num3,num1,n1: integer; oper1,oper2, numstr1, numstr2, numstr3: string;
  begin
    n1:=0;
    num1:= 0;
    num2:= 0;
    num3:= 0;
    oper1:='';
  while(i <= length(s)) do
  begin
    if n1<> 0 then
    begin
      if (s[i]<>'*') then
      begin
        num1:= arithmetic(n1,num1,oper1);
        n1:=0;
        oper1:='';
      end
      else
      begin
        num2:= num1;
        num1:= n1;
        n1:= 0;
      end;
    end;
    if (s[i]>= '0') and (s[i] <= '9') then
    begin
      if (num1 = 0) then
      begin
        numstr1:= s[i];
        val(numstr1,num1,Code);
        if ((i=length(s)) and (n1 <> 0)) then
          n1:= arithmetic(n1,num1,oper1);
      end
      else
      begin
        numstr2:= s[i];
        val(numstr2,num2,Code);
        if (i = length(s))or(oper1 = '*')or(s[i+1]= ')') then
        begin
          num1:= arithmetic(num1,num2,oper1);
          num2:= 0;
          oper1:= '';
          if (s[i+1]=')')then
          begin
            if (i+1<>length(s)) then
              inc(i);
            break;
          end;
        end;
      end;
    end
    else if(s[i] = '(') then
    begin
      if i<>1 then
        n1:= num1;
      inc(i);
      num1:= calc(i,s);
    end
    else if(s[i] = ')') then
    begin
      if i = length(s) then
        dec(i);
      if num2<>0 then
        num1:= arithmetic(num1,num2,oper1);
      num2:= 0;
      oper1:= '';
      break;
    end
    else if (oper1 = '') then
      oper1:= s[i]
    else
    begin
      oper2:= s[i];
      if (oper2 <> '*') then
      begin
        num1:= arithmetic(num1,num2,oper1);
        numstr2:='';
        oper1:= oper2;
        oper2:= '';
        num2:=0;
      end
      else
      begin
        if(s[i+1] <> '(') then
        begin
          inc(i);
          numstr3:= s[i];
          val(numstr3,num3,Code);
          num2:= arithmetic(num2,num3,oper2);
          num3:= 0;
          oper2:= '';
          num1:= arithmetic(num1,num2,oper1);
          num2:= 0;
          oper1:= '';
          break;
        end
        else
        begin
          inc(i,2);
          num3:= calc(i,s);
          num2:= arithmetic(num2,num3,oper2);
          num3:= 0;
          oper2:= '';
          if (i = length(s)) then
          begin
            num1:= arithmetic(num1,num2,oper1);
            num2:= 0;
            oper1:= '';
          end;
        end;
        end;
      end;
      inc(i);
    end;
    if n1 = 0 then
      result:= num1
    else
      result:= n1;
  end;

  begin
    i:= 1;
    s:= '5*(3+4)-7*9';
    write(s);
    write(' = ',calc(i,s));
    readln();
  end.
vedro-compota's picture

По-идее алгоритмически рекурсиный вызов достаточно делать когда встречаешь открывающую скобку как в первом случае, зачем такие вызовы по всех остальных случаях?

if(s[i] = '(') then
    begin
      if i<>1 then
        n1:= num1;
      inc(i);
      num1:= calc(i,s);

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

var
  s:string;
  i:integer;
function calc(var i:integer;s:string):integer;
var
  str:string;
  a,midnes: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 :=calc(i,s);
            i +=1;
          end
          else
          val(str,a);

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

           if (s[i] = '*') then
            begin
              mult := true;
              midnes := 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,' = ', calc(i, s));
  readln();
end.