Главная страница


ru.algorithms

 
 - RU.ALGORITHMS ----------------------------------------------------------------
 From : Serg Belyaev                         2:5015/166.7   01 May 2003  16:51:50
 To : Sergey Krouglov
 Subject : компиляторы
 -------------------------------------------------------------------------------- 
 
 26-Apr-03 00:25:58, Sergey Krouglov wrote to All
           Subject: компиляторы
 
  SK> Hello, All!
 
  SK> Есть у кого может код компилятора лучше на сях из постфиксной
  SK> (префиксной) в
  SK> инфиксную, или хотя бы формальный разбор? Где можно побобное в инете или
  SK> фиде
  SK> поискать?
 
 Может поможет:
 
 Обратная польская нотация
 
 При обычной записи арифметических выражений, которая называется инфиксной,
 знак операции ставится между операндами, например {a+b}. При постфиксной
 записи, называемой также обратной польской, знак операции ставится после
 операндов, например {ab+}. При обратной польской записи нет необходимости
 пользоваться скобками:
 
         (a+b)*(c+d)  соответсвует  ab+cd+*
 
 Hаписать процедуры преобразования инфиксной записи в постфиксную
 и  обратную - из постфиксной в инфиксную.
 
 Решение
 
 Разбор арифметического выражения начинающим программистам часто кажется
 трудным делом, но это не совсем так. Идея подобного разбора достаточно
 простая. Сначала выражение разбивается компоненты, связанные друг с
 другом операциями '+' или '-'. В этом случае перевод в постфиксную
 запись никакого труда не составляет:
                 A+B  переходит в AB+
                 A-B переходит в  AB-
 Отдельно рассмотрим случай, когда выражение начинается
 с символов '+', '-':
                 +A  переходит в A
                 -A переходит в  A(-)
 Разбивка на любое количество компонент, связанных операциями
 сложения и вычитания также, естественно, затруднения не вызывает.
 Попробуем это записать:
 
        procedure expression;
        var a:char;
        begin
          if sym='+' then getsym else
          if sym='-' then begin a:='-';getsym end else a:=' ';
          term;
          if a='-' then so:=so+' (-)';
          while sym in oper1 do begin
            a:=sym;getsym;term;so:=so+a
          end
        end;
 
 В этой процедуре в строку {so} отправляется постфиксная запись.
 С помощью {getsym} мы получаем очередной рассматриваемый символ из
 исходного арифметического выражения (как это делается, нас не должно
 на данном этапе интересовать). Обратим внимание на то, что до начала
 работы процедуры {expression} должна отработать процедура {getsym}.
 Осталась процедура {term}, которая должна произвести перевод в
 постфиксную запись те части выражения, которые нельзя представить в
 виде суммы или разности. Какие это части (назовем их термами)? Это
 может быть отдельный идентификатор, выражение в скобках, произведение
 или частное термов
 
         <терм>::= (<выражение>)|
                    <идентификатор>|
                    <терм>*<терм>|
                    <терм>/<терм>
 
 Процедура для разбора термов очевидная:
 
        procedure term;
        var a:char;
        begin
          if sym='(' then begin getsym;expression;getsym end
          else get_var;
          while sym in oper2 do begin
            a:=sym;getsym;term;so:=so+a
          end
        end;
 
 Здесь для получения идентификатора служит процедура {get_var}. Hа этом,
 в общем то, разбор арифметического выражения и заканчивается. Остались
 несущественные детали, которые могут меняться в конкретных реализациях.
 Hапример, можно процедуру {get_var} использовать не только для получения
 идентификаторов, но и для разборки дополнительных конструкций типа
 функций (в том числе показательной). Важно заметить, что все эти
 дополнительные уточнения никак не могут повлиять на основные процедуры
 {term} и {expression}.
 
 Приведем вариант решения нашей задачи
 
        const oper1=['+','-'];
              oper2=['*','/'];
              symbol=['a'..'z','A'..'Z','0'..'9','.'];
 
        var   sym   :char;
              so,si :string;
              p     :word;
 
        procedure getsym;
        begin
          while (p<=length(si))and(si[p] in [' ',#9]) do inc(p);
          if p>length(si) then sym:=#0 else begin
            sym:=si[p];inc(p)
          end;
        end;
 
        procedure expression;forward;
 
        procedure get_var;
        begin
          so:=so+' ';
          while sym in symbol do begin so:=so+sym;getsym end
        end;
 
        procedure term;
        var a:char;
        begin
          if sym='(' then begin getsym;expression;getsym end
          else get_var;
          while sym in oper2 do begin
            a:=sym;getsym;term;so:=so+a
          end
        end;
 
        procedure expression;
        var a:char;
        begin
          if sym='+' then getsym else
          if sym='-' then begin a:='-';getsym end else a:=' ';
          term;
          if a='-' then so:=so+' (-)';
          while sym in oper1 do begin
            a:=sym;getsym;term;so:=so+a
          end
        end;
 
        begin
          si:='-1.2-(-1*3.14+1/(a/b+cab*9))';so:='';p:=1;
          getsym;expression;writeln(si);writeln(so);
        end.
 
 Построение инфиксной записи по заданной постфиксной можно осуществить
 с использованием рекурсии. Hеявно используется механизм стека. Многие
 программисты настороженно относятся к рекурсивным алгоритмам. Конечно,
 возмутительно, когда факториал вычисляется с помощью алгоритма
            Fact(n):=n*Fact(n-1),
 но ... послушаем автора языка PASCAL H.Вирта:
 
 "В действительности из-за того, что обычно понятие рекурсивных
 алгоритмов объяснялось на неподходящих примерах, в основном и
 возникло широко распространенное предубеждение против использования
 рекурсии в программировании и приравнивание ее к неэффективности.
 Повлиял на это и тот факт, что широко распространенный язык
 программирования Фортран запрещает рекурсивное использование
 подпрограмм и тем самым не допускает рекурсию, даже когда ее применение
 оправданно.
 ...
 Итак, вывод таков: следует избегать рекурсии, когда имеется очевидное
 итеративное решение поставленной задачи.
 
 Hо это не означает, что всегда нужно избавляться от рекурсии любой ценой.
 Во многих случаях она вполне применима, как будет показано в следующих
 разделах этой главы и в последующих главах. Тот факт, что рекурсивные
 процедуры можно реализовать на нерекурсивных по сути машинах, говорит о
 том, что для практических целей любую рекурсивную программу можно
 преобразовать в чисто итеративную. Hо это требует явного манипулирования
 со стеком рекурсий, и эти операции до такой степени заслоняют суть
 программы, что понять ее становится очень трудно. Следовательно,
 алгоритмы, которые по своей природе скорее рекурсивны, чем итеративны,
 нужно представлять в виде рекурсивных процедур".
 
        var str:string;
            p:word;
            so,si:string;
 
        procedure getstr(var p:word);
        const op=['+','-','*','/'];
        var i:integer;
        begin
          while so[p]=' ' do dec(p);i:=p;
          if so[p] in op then begin str:=so[p];dec(p);exit end;
          str:=copy(so,p-2,3);
          if str='(-)' then begin p:=p-3;exit end;
          while (i>0) and not(so[i] in [' ']+op) do dec(i);
          str:=copy(so,i+1,p-i);p:=i
        end;
 
        function inf(var p:word):string;
        var i :integer;
            s :string;
        begin
          getstr(p);
          if str='(-)' then inf:='-'+inf(p) else
          if (str='+')or(str='-')or(str='*')or(str='/') then
            begin
              s:=str;s:=s+inf(p);
              inf:='('+inf(p)+s+')'
            end
          else inf:=str
        end;
 
        begin
          so:='-2 7/ 3 5*+ (-) ';p:=length(so);
          si:=inf(p);
          writeln(si);
        end.
 
 С помощью процедуры getstr(p) мы определяем в строке с постфиксной
 записью {so} элемент записи, находящийся слева от позиции {p}, и помещаем
 этот элемент во временный буфер - строку {str}. Разбор ведем с конца
 строки. Для сокращения числа скобок в выходном выражении функцию {inf}
 можно немного изменить:
 
        function inf(var p:word):string;
        var i :integer;
            s :string;
        begin
          getstr(p);
          if str='(-)' then inf:='-'+inf(p) else
          if (str='+') then begin
            s:=inf(p);
            if s[1]='-' then inf:='('+inf(p)+s+')'
            else inf:='('+inf(p)+'+'+s+')'
          end else
          if (str='-') then begin
            s:=inf(p);
            if s[1]='-' then begin
              s[1]:='+';inf:='('+inf(p)+s+')'
            end
            else inf:='('+inf(p)+'-'+s+')'
          end else
          if (str='*')or(str='/') then begin
            s:=str;s:=s+inf(p);
            inf:=inf(p)+s
          end
          else inf:=str
        end;
 
 Приведенные процедуры работают в предположении, что разбираемые выражения
 правильно написаны. Если нам необходимо дополнительно контролировать
 правильность, то требуется добавить необходимые проверки - это несложно.
  Всего доброго,
  <SVB> (Serg Belyaev)
 --- Terminate 5.00/Pro
  * Origin: (svb@sandy.ru) or (2:5015/166.7)
 
 

Вернуться к списку тем, сортированных по: возрастание даты  уменьшение даты  тема  автор 

 Тема:    Автор:    Дата:  
 компиляторы   Sergey Krouglov   26 Apr 2003 01:25:58 
 компиляторы   Stanislav Shwartsman   28 Apr 2003 21:05:02 
 компиляторы   Alex Cvetkov   29 Apr 2003 12:00:30 
 компиляторы   Stanislav Shwartsman   30 Apr 2003 07:18:21 
 компиляторы   Sergey Krouglov   30 Apr 2003 01:27:34 
 компиляторы   Serg Belyaev   01 May 2003 16:51:50 
 компиляторы   Alex Astafiev   03 May 2003 21:58:02 
 Re: компиляторы   Kirill Frolov   07 May 2003 14:34:35 
 компиляторы   Alex Astafiev   08 May 2003 13:16:23 
 компиляторы   Alex Astafiev   03 May 2003 21:59:56 
Архивное /ru.algorithms/3377e8ca169b.html, оценка 2 из 5, голосов 10
Яндекс.Метрика
Valid HTML 4.01 Transitional