Тема 6. Использование линейных связанных списков

6.1. Вычисления арифметических выражений

Одной из задач при разработке трансляторов является задача расшифровки арифметических выражений, например, вида:

r:=(a+b)*(c+d)-e;

Запись выражения вида a+b называется инфиксной формой, причем , a и b операнды, + операция. Возможны также другие формы записи этого выражения: +ab префиксная, ab+ постфиксная. В наиболее распространенной инфиксной форме для указания последовательности выполнения операций необходимо расставлять скобки. Польский математик Я. Лукашевич обратил внимание на тот факт, что при записи выражений в постфиксной форме скобки не нужны, а последовательность операндов и операций удобна для расшифровки основанной на применении эффективных методов. Поэтому постфиксная запись выражений получила название обратной польской записи (ОПЗ). Например, вышеприведенное выражение в ОПЗ выглядит следующим образом:

r=ab+cd+*e;

Решение задач преобразования инфиксного выражения в постфиксную запись и вычисление выражения в ОПЗ оформим виде класса с соответствующими методами. Используем ранее описанный класс Tlist работы с однонаправленным списком, в котором поле Inf имеет тип Char.

Type Tpz=class

mszn:array[’a’..’я’]of extended;//[a..z..я]

stec:Tlist;

Procedure OBP(Var stri,strp:string);//преобразование

Function AV(strp:string):extended; //вычисление

End;

Алгоритм вычисления выражения в ОПЗ основан на использовании стека. При просмотре выражения слева направо каждый операнд заносится в стек. В результате для каждой операции, относящиеся к ней операнды будут двумя верхними элементами стека. Берем из стека эти операнды, выполняем очередную операцию над этими операндами и результат помещаем в стек. Приведенный ниже метод AV(..) реализует этот алгоритм.

Function Tpz.AV(strp:string):extended;//в strp выражение в ОПЗ

Var ch,ch1,ch2,chr:char;

op1,op2,rez:extended;

begin

stec:=Tlist.create;

chr:=Succ(’z’); //следующий за z символ

for i:=1 to Length(str)do

begin

ch:=strp[i];

if not ch in [’*’,’/’,’+’,’-’,’^’]

then stec.Add1(ch)

else

begin

stec.Read1(ch2); stec.Read1(ch1);

op1:=mszn[ch1];op2:=mszn[ch2];

case ch of

’+’:rez:=op1+op2;

’-’:rez:=op1-op2;

’*’:rez:=op1*op2;

’/’:rez:=op1/op2;

’^’:rez:=exp(op2*ln(op1));

end;

mszn[chr]:=rez; stec.Add1(chr);Inc(chr);

end; // for

Result:=rez; stec.Free;

end;// AV

Пример вычисления выражения:

Var pz:Tpz;

...

Begin

pz:=Tpz.create;

pz.mszn[’a’]:=0.1; pz.mszn[’b’]:=5.2; pz.mszn[’c’]:=2.4; pz.mszn[’d’]:=6;

pz.mszn[’e’]:=8.4;

write(pz.AV(’ab+cd+*e-’));// вывод результата AV

...pz.free;

end;

Алгоритм преобразования выражения из инфиксной формы в форму обратной польской записи (постфиксную) был предложен Дейкстрой. При его реализации вводится понятие стекового приоритета операций:

Операции )( + – */ ^ (^ - возведение в степень)

Приоритет 0 1 2 3

Просматривается слева направо исходная строка символов, в которой записано выражение в инфиксной форме, причем операнды переписываются в выходную строку в которой формируется постфиксная форма выражения, а знаки операций заносятся в стек следующим образом:

  1. Если стек пуст, то операция записывается в стек;

  2. Открывающаяся скобка дописывается в стек;

  3. Закрывающая скобка выталкивает в выходную строку все операции из стека до ближайшей открывающейся скобки, сами скобки уничтожаются;

  4. Если стек не пуст, то очередная операция выталкивает в выходную строку все операции из стека с большим чем у нее или равным приоритетом до первой менее приоритетной операции, причем сама дописывается в стек;

  5. Если после выборки из исходной строки последнего символа в стеке остались операции, то все они выталкиваются в выходную строку.

// в stri- инфиксное, в strp- постфиксное выражения

Procedure Tpz.OBP(Var stri,strp:string);

Function prior(ch:char):byte;

begin

case ch of

’(’,’)’:prior:=0;

’+’,’-’:prior:=1;

’*’,’/’:proir:=2;

’^’:proir:=3;

end;

end;

Var pc:0..3;