Урок 18. Задача 11.

Урок 18. Задача 11.

Задана последовательность символов, имеющая следующий вид: p1q1p2q2p3...qn–1pn , где pi — число, а qi — знак арифметического действия из набора {+, –, *}. Вычислите значение выражения, предполагая, что действия выполняются согласно правилам арифметики.
Входные данные: На вход программе подается строка указанного вида, состоящая не более чем из 9 чисел, разделенных символами арифметических операций.
Выходные данные: Выведите значение арифметического выражения.
Как решать: Посчитайте результат за одно прочтение строки, без использования массивов (в нашем курсе решение в таком стиле для этой задачки было впервые добавлено участником slavina036).
(условие задачи взято из книги Е. В. Андреевой)

Var i, result, num_mul, vl_I, vl_C :integer;
    s, str, chr :string;
    fl_mul, fl_minus :boolean;

begin
  s := '2*2*1-4-4+4+2*4';
  chr := '';  // знак
  result := 0;  // ответ
  num_mul := 0;  // число из предыдущей итерации
  fl_mul := false;  // флаг умножения
  fl_minus := false;  // флаг для получения отрицательного числа 
  str := '';  // строка с актуальным числом

  for i := 1 to length(s) + 1 do
  begin
    if i = length(s) + 1  then
      s[i] := '+';

    val(s[i], vl_I, vl_C);
    if vl_C = 0 then  // ищет число
      str := str + s[i]
    else
    begin
      chr := s[i];  // находит знак
      val(str, vl_I);
      str := '';

      if fl_mul then  // умножает
      begin
        fl_mul := false;
        vl_I := num_mul * vl_I;
      end
      else
        if fl_minus then  // делает число отрицательным
        begin
          fl_minus := false;
          vl_I := vl_I * (-1);
        end;

      if chr = '*' then
      begin
        fl_mul := true;
        num_mul := vl_I;
      end
      else
      begin
        if chr = '-' then
          fl_minus := true;
        result := result + vl_I;
      end;
    end;
  end;
  write(s, ' = ', result);
  readln();
end.
vedro-compota's picture

Переписать так, чтоб val(s[i], vl_I, vl_C); вызовалось только когда действительно ожидается что там число, и когда уже сформировано, т.е. напр. если выражение:

2+444

-- для '444' операция val() должна быть вызвана 1 раз

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

Var i, result, num_mul, vl_I, vl_C :integer;
    s, str, chr :string;
    fl_mul, fl_minus :boolean;
 
begin
  s := '2*2*1-4-4+4+2*4';
  chr := '';  // знак
  result := 0;  // ответ
  num_mul := 0;  // число из предыдущей итерации
  fl_mul := false;  // флаг умножения
  fl_minus := false;  // флаг для получения отрицательного числа 
  str := '';  // строка с актуальным числом
 
  for i := 1 to length(s) + 1 do
  begin
    if i = length(s) + 1  then
      fl_minus := false;
 
    if (s[i] >= '0')  and (s[i] <= '9') then  // ищет число
      str := str + s[i]
    else
    begin
      chr := s[i];  // находит знак
      val(str, vl_I);
      str := '';
 
      if fl_mul then  // умножает
      begin
        fl_mul := false;
        vl_I := num_mul * vl_I;
      end
      else
        if fl_minus then  // делает число отрицательным
        begin
          fl_minus := false;
          vl_I := vl_I * (-1);
        end;
 
      if chr = '*' then
      begin
        fl_mul := true;
        num_mul := vl_I;
      end
      else
      begin
        if chr = '-' then
          fl_minus := true;
        result := result + vl_I;
      end;
    end;
  end;
  write(s, ' = ', result);
  readln();
end.
vedro-compota's picture

  1. решить без флагов, для отложенных действий сохранять символ последней операции выполнение операции для двух аргументов как char
  2. написать функцию, куда можно передать два числа и знак операции, а на выход она вернет результат

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

Var i, result, remember, number :integer;
    s, str, chr :string;

function result_int(int_1, int_2 :integer; chr :string) :integer;
begin
  if chr = '*' then
    result_int := int_1 * int_2
  else
    if chr = '-' then
      result_int := int_1 - int_2
    else
      result_int := int_1 + int_2;
end;

begin
  s := '-2*2*1*1+0+8-2-2+0-0';
  chr := '';  // знак
  result := 0;  // ответ
  remember := 0;  // число из предыдущей итерации
  str := '';  // строка с актуальным числом

  for i := 1 to length(s) + 1 do
  begin
    if i > length(s) then
      s[i - 1] := s[i - 1];

    if (s[i] >= '0')  and (s[i] <= '9') then  // ищет число
      str := str + s[i]
    else
    begin
      val(str, number);
      str := '';
      result := result_int(remember, number, chr);
      writeln(result);
      chr := '';

      if s[i] = '*' then
      begin
        remember := result;
        chr := '*';
      end
      else
      begin
        if s[i] = '-' then
          chr := '-';
        remember := result;
      end;
    end;
  end;
  write(s, ' = ', result);
  readln();
end.   
В виде функции
Var i, result, remember, number :integer;
    s, str, chr :string;

function result_int(int_1, int_2 :integer; chr :string) :integer;
begin
  if chr = '*' then
    result_int := int_1 * int_2
  else
    if chr = '-' then
      result_int := int_1 - int_2
    else
      result_int := int_1 + int_2;
end;

function calculator(s :string; i :integer) :integer;
begin
  while i < length(s) do
  begin
    if (s[i] >= '0')  and (s[i] <= '9') then  // ищет число
    begin
      str := str + s[i];
      i += 1;
    end
    else
    begin
      val(str, number);
      str := '';
      result := result_int(remember, number, chr);
      chr := '';

      if s[i] = '*' then
      begin
        remember := result;
        chr := '*';
        i += 1;
      end
      else
      begin
        if s[i] = '-' then
          chr := '-';
        i += 1;
        remember := result;
      end;
    end;
  end;
  calculator := result;
end;

begin
  s := '-2*2*1+0+8-2-2*1+0-0';
  i := 1;
  chr := '';  // знак
  result := 0;  // ответ
  remember := 0;  // число из предыдущей итерации
  str := '';  // строка с актуальным числом
  result := calculator(s, i);
  write(s, ' = ', result);
  readln();
end.  
vedro-compota's picture

  1. Проверить работоспособность в целом для примера 1+2*2+1
  2. Функция должна принимать на вход только переменные, которые действительно нужно передавать снаружи, для всего остального есть локальные переменные подпрограммы см. например http://fkn.ktu10.com/?q=node/15820
  3. Убрать обращения к глобальным переменным
  4. Убрать дублирование инкрементирования счетчика позиции в строке, или объяснить почему это не возможно
  5. Дублирование remember := result;
  6. Т.к. стока считается корректной - смысла проверять что за символ операции мы встретили нет:
          if s[i] = '*' then
          begin
            remember := result;
            chr := '*';
            i += 1;
          end
          else
          begin
            if s[i] = '-' then
              chr := '-';
            i += 1;
            remember := result;
          end;
    

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

Var i, result, remember, number :integer;
    s, str, chr :string;

function result_int(int_1, int_2 :integer; chr :string) :integer;
begin
  if chr = '*' then
    result_int := int_1 * int_2
  else
    if chr = '-' then
      result_int := int_1 - int_2
    else
      result_int := int_1 + int_2;
end;

function calculator(s :string) :integer;
begin
  chr := '';  // знак
  result := 0;  // ответ
  remember := 0;  // число из предыдущей итерации
  str := '';  // строка с актуальным числом
  while i < length(s) do
  begin
    if (s[i] >= '0')  and (s[i] <= '9') then  // ищет число
      str := str + s[i]
    else
    begin
      val(str, number);
      str := '';
      result := result_int(remember, number, s[i]);
    end;
    i += 1;
  end;
  calculator := result;
end;

begin
  s := '1+2*3+1';
  result := calculator(s);
  write(s, ' = ', result);
  readln();
end.
vedro-compota's picture

0+  1+2+2
0+  1+2*2*2-4+6*4*5+

- обрабатываем умножение

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

Var i, j, result, remember, number, multy :integer;
    s, str, chr :string;
    fl :boolean;

function calculator(s :string) :integer;
begin
  j := 1;
  i := 1;
  chr := '+';  // знак
  result := 0;  // ответ
  remember := 0; // число из предыдущей итерации
  multy := 0;
  number := 0;
  fl := false;
  str := '';  // строка с актуальным числом
  while i < length(s) + 2 do
  begin
    if i > length(s) then
    begin
      s[i - j] := s[i - j];
      j += 1;
    end;

    if (s[i] >= '0')  and (s[i] <= '9') then  // ищет число
      str := str + s[i]
    else
    begin
      val(str, number);
      str := '';

      if fl then
      begin
        number := remember * number;
        fl := false;
      end;

      if s[i] = '*' then
      begin
        remember := number;
        fl := true;
      end
      else
      begin
        if chr = '-' then
          result := result - number
        else
          result := result + number;
        chr := s[i];
      end;
      writeln(result, '_res ', chr, '_chr ', remember, '_rem ', number, '_num ', multy, '_mul ', i, '_i ');
    end;
    i += 1;
  end;
  calculator := result;
end;

begin
  s := '-1+3*3+1+1*1*1';
  result := calculator(s);
  write(s, ' = ', result);
  readln();
end.   
vedro-compota's picture

нельзя уходить от декомпозиции в пользу более длинного блока

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

Var i, j, result, remember, number, multy :integer;
    s, str, chr :string;
    fl :boolean;



function result_int(int_1, int_2 :integer; chr :string) :integer;
begin
  if chr = '*' then
    result_int := int_1 * int_2
  else
    if chr = '-' then
      result_int := int_1 - int_2
    else
      result_int := int_1 + int_2;
end;

function calculator(s :string) :integer;
begin
  j := 1;
  i := 1;
  chr := '+';  // знак
  result := 0;  // ответ
  remember := 0; // число из предыдущей итерации
  multy := 0;
  number := 0;
  fl := false;
  str := '';  // строка с актуальным числом
  while i < length(s) + 2 do
  begin
    if i > length(s) then
    begin
      s[i - j] := s[i - j];
      j += 1;
    end;

    if (s[i] >= '0')  and (s[i] <= '9') then  // ищет число
    begin
      str := str + s[i];
      writeln(result, '_res ', chr, '_chr ', remember, '_rem ', number, '_num ', multy, '_mul ', i, '_i ');
    end
    else
    begin
      val(str, number);
      str := '';

      if fl then
      begin
        number :=  result_int(remember, number, chr);
        fl := false;
        multy := number;
        chr := s[i];
      end;

      if s[i] = '*' then
      begin
        remember := number;
        fl := true;
      end
      else
        result :=  result_int(result, number, chr);
      chr := s[i];

    end;
    i += 1;
  end;
  calculator := result;
end;

begin
  s := '-1+2*3*1+1';

  result := calculator(s);
  write(s, ' = ', result);
  readln();
end. 
vedro-compota's picture

1) Вынести получение очередного числа в процедуру
2) Завести единственную переменную для знака операции и проверять в цикле только ее
3)

while i < length(s) + 2 do

-- исправить
4) Убрать лишние переменные и уточнить назначение имеющихся в комментариях

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

Var i, j, c, n, result, remember, number, multy :integer;
    s, str, chr :string;
    fl :boolean;

procedure get_value(s :string; var n, i :integer);
var fl_number :boolean;
begin
  fl_number := false;
  for j := 1 to length(s) do
  begin
    if (s[i] >= '0') and (s[i] <= '9') then  // ищет число
    begin
      str := str + s[i];
      fl_number := true
    end
    else
      if fl_number then
      begin
        val(str, n, c);
        break;
      end;
    i += 1;
  end;
end;

function result_int(int_1, int_2, int_3 :integer; chr :string) :integer;
begin
  if chr = '*' then
    result_int := int_2 * int_3
  else
    if chr = '-' then
      result_int := int_1 - int_2
    else
      result_int := int_1 + int_2;
end;

function calculator(s :string) :integer;
begin
  j := 1;
  i := 1;
  chr := '+';  // знак
  result := 0;  // ответ
  remember := 0; // число из предыдущей итерации
  multy := 0;
  number := 0;
  fl := false;
  //str := '';
  while i <= length(s) do
  begin
    get_value(s, n, i);
    result := result_int(result, n, multy, chr);
    //writeln(n, ' ', ' chr', chr, ' i', i, ' ', s[i], ' r', result);
    chr := s[i];
    if chr = '*' then
      multy := n;
  end;
  calculator := result;
end;

begin
  s := '2*4*3';
  result := calculator(s);
  write(s, ' = ', result);
  readln();
end.  
vedro-compota's picture

1)

function result_int(int_1, int_2, int_3 :integer; chr :string) :integer;
begin
  if chr = '*' then
    result_int := int_2 * int_3
  else
    if chr = '-' then
      result_int := int_1 - int_2
    else
      result_int := int_1 + int_2;
end;

-- не усложняем, сигнатуру, если надо вынести умножение, то используем обертку, для тренировки решить отдельно задачу 3 урока 16 http://fkn.ktu10.com/?q=node/13330

2)

procedure get_value(s :string; var n, i :integer);
var fl_number :boolean;
begin
  fl_number := false;
  for j := 1 to length(s) do
  begin
    if (s[i] >= '0') and (s[i] <= '9') then  // ищет число
    begin
      str := str + s[i];
      fl_number := true
    end
    else
      if fl_number then
      begin
        val(str, n, c);
        break;
      end;
    i += 1;
  end;
end;

1.1) лучше перейти на while
1,2) конвертировать число после окончания цикла

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

Var i, c, n, result, multy: integer;
    s, str: string;
    symb: char;
    fl: boolean;

procedure get_value(s :string; var n, i :integer);
begin
  str := '';  // запись числа
  while i < length(s) do
  begin
    i += 1;  // итерации
    if (s[i] >= '0') and (s[i] <= '9') then  // ищет число
      str := str + s[i]
    else
      break  // стоп цикла
  end;
  val(str, n, c);
end;

function result_int(int_1, n: integer; symb: char): integer;
begin
  if symb = '*' then
    result := int_1 * n
  else
    result := int_1 + n;
end;

function calculator(s: string): integer;
begin
  i := 0;
  symb := '+';  // знак
  result := 0;  // ответ
  multy := 1;   // число для умножения из памяти
  fl := false;  // флаг для умножения
  while i < length(s) do
  begin
    get_value(s, n, i);  // получить число

    if symb = '-' then  // делает число отрицательым
      n := -n;

    if fl then
    begin
      n := result_int(multy, n, symb);
      fl := false;
    end;

    symb := s[i];  // текущий символ
    if symb = '*' then
    begin
      multy := n;
      fl := true;
    end
    else
      result := result_int(result, n, symb);
  end;
  calculator := result;
end;

begin
  s := '-1-3*3*2-1';
  result := calculator(s);
  write(s, ' = ', result);
  readln();
end.      
vedro-compota's picture

1) записать алгоритм решения задачи текстом

Начать можно так:

Берем текстовую строку
Далее в цикле:

  1. Считываем очередноеЧисло, сдвигаем указательСтроки на сл. символ
  2. Считываем очереднойЗнакОперации, сдвигаем указательСтроки на сл. символ
  3. .............

2) изменить работу с счетчиками:

get_value(s, n, i);  // получить число

-- i должно стоять на первом элементе предполагаемого числа
3) Добавить процедуру для получения очередного знака операции

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

Алгоритм программы "калькулятор".
1) Извлекает число из строки(увеличение счетчика).
2) Затем проверяет, если знак предыдущей итерации " - ", то
делает число отрицательным.
3) Далее проверка на умножение, если флаг "истина", то умножает
число из памяти на текущее число.
4) Извлекает знак из строки(увеличение счетчика).
5) Проверяет, если знак " * ", то записывает текущее число в память для умножения,
иначе складывает его с предыдущем(результат) числом.

Var i, c, n, result, multy: integer;
    s, str: string;
    symb: char;
    fl: boolean;

procedure get_char(s :string; var i: integer; var symb: char);
var j: integer;  // индекс в массиве cA;
    cA: array [1..4] of char = ('-', '+', '*', '/');
begin
  while i <= length(s) do
  begin
    for j := 1 to 4 do
      if s[i] = cA[j] then
      begin
        symb := cA[j];
        i += 1;
        Exit;
      end;
  end;
end;

procedure get_value(s :string; var n, i :integer);
begin
  str := '';  // запись числа
  while i <= length(s) do
  begin
    if (s[i] >= '0') and (s[i] <= '9') then  // ищет число
    begin
      str := str + s[i];
      i += 1;
    end
    else
      break;  // стоп цикла
  end;
  val(str, n, c);
end;

function result_int(int_1, n: integer; symb: char): integer;
begin
  if symb = '*' then
    result := int_1 * n
  else
    result := int_1 + n;
end;

function calculator(s: string): integer;
begin
  i := 1;
  result := 0;  // ответ
  fl := false;  // флаг для умножения
  while i <= length(s) do
  begin
    get_value(s, n, i);  // получить число

    if symb = '-' then  // если знак предыдущей итерации " - ",
      n := -n;          // то делает число отрицательным

    if fl then
    begin
      n := result_int(multy, n, symb);
      fl := false;
    end;

    get_char(s, i, symb);  // текущий символ
    if symb = '*' then
    begin
      multy := n;
      fl := true;
    end
    else
      result := result_int(result, n, symb);
  end;
  calculator := result;
end;

begin
  s := '1+11*3*2-77';
  result := calculator(s);
  write(s, ' = ', result);
  readln();
end.   
vedro-compota's picture

засчитано, можно переходить к рекурсивному варианту

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