ТА_Методички / Lec_7
.pdf
Лекція № 7
Побудова зворотного польського запису
Однією з головних причин, що лежать в основі появи мов програмування високого рівня, є завдання, що вимагають великих обсягів обчислень. Тому до мов програмування ставляться вимоги максимального наближення форми запису обчислень до природної мови математики.
У цьому зв'язку однією з перших областей системного програмування сформувалося дослідження способів трансляції виразів. Тут отримані численні результати, однак найбільше поширення одержав метод трансляції за допомогою зворотного польського запису, що запропонував польський математик Я. Лукашевич.
Нехай задано простий арифметичний вираз:
|
. |
(1) |
(A + B) *(C + D) −E |
Представимо цей вираз у вигляді дерева, у якому вузлам відповідають операції, а листю - операнди. Побудову почнемо з кореня, у якості якого вибирається операція, що виконується останньою. Лівій вітці відповідає лівий операнд операції, а правій вітці - правий.
Дерево виразу (1) показане на Рис. 4. |
Рис. 4 |
21/28
Дерева і графи в мові програмування С
Зробимо обхід дерева, під яким будемо розуміти формування рядка символів із символів вузлів і віток дерева. Обхід будемо робити від самої лівої вітки вправо й вузол переписувати у вихідний рядок тільки після розгляду всіх його віток. Результат обходу дерева має вигляд:
|
. |
(2) |
AB +CD +*E − |
Характерні риси виразу (2) полягають у проходженні символів операцій за символами операндів і у відсутності дужок. Такий запис називається зворотним польським записом.
Зворотний польський запис володіє рядом позитивних властивостей, які перетворюють його в ідеальну проміжну мову при трансляції.
По-перше, обчислення виразу, записаного у зворотному польському записі, може проводитися шляхом однократного перегляду, що є досить зручним при генерації об'єктного коду програм.
Наприклад, обчислення виразу (2) може бути проведене в такий спосіб:
N |
Аналізований |
Дія |
|
рядок |
|
1 |
AB+CD+* E- |
rl=A+B |
2 |
Rl CD+* E - |
r2=C+D |
3 |
r1 r2*E - |
r1=r1 * r2 |
4 |
r1 E - |
r1=r1 -E |
5 |
r1 |
|
22/28
|
|
Лекція № 7 |
|
Тут r1, r2 – додаткові змінні. |
|
Таблиця 1. |
|
По-друге, одержання зворотного польського запсу з |
|
||
Операція |
|
Пріорітет |
|
вихідного виразу може здійснюватися досить просто. |
) |
|
0 |
Для цього вводиться поняття стекового пріоритету |
( |
|
1 |
операцій (Табл. 1). |
+ - |
|
2 |
Проглядається вхідний рядок символів зліва направо, |
|
|
|
операнди нереписуються у вихідний рядок, а знаки |
* / |
|
3 |
операцій заносяться в стек на основі наступних міркувань: |
|
|
|
а) якщо стек порожній, то операція з вхідного рядка записується в стек;
б) операція виштовхує зі стека всі операції з більшим або рівним пріоритетом у вихідний рядок;
в) якщо черговий символ з вхідного рядка є відкриваюча дужка, то вона проштовхується в стек;
г) закриваюча кругла дужка виштовхує всі операції зі стека до найближчої відкриваючої дужки, самі дужки у вихідний рядок не переписуються, а видаляються.
23/28
Дерева і графи в мові програмування С
Процес одержання зворотного польського запису виразу (1) представлений у Табл. 2:
|
|
|
|
|
|
6 |
|
|
|
|
|
Таблиця 2. |
|||
Символ, |
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
|
|
що |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
переглядає |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ться |
|
|
|
|
|
* |
|
|
|
|
|
|
|
|
|
Вхідний |
( |
А |
+ |
В |
) |
( |
C |
+ |
D |
) |
- |
Е |
|
|
|
рядок |
|
|
|
|
|
* |
|
|
|
|
|
|
|
|
|
Стан стека |
( |
( |
+ ( |
+ ( |
|
(* |
(* |
+ |
+ |
* |
- |
- |
|
|
|
|
|
|
|
|
|
|
|
|
(* |
(* |
|
|
|
|
|
Вихідний |
|
А |
|
B |
+ |
|
|
C |
|
D |
+ |
* |
Е |
- |
|
рядок |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24/28
Лекція № 7
// Приклад: текст програми; яка виконує задані дії
#іnclude <stdіo.h>
#іnclude <stdlіb.h>
#іnclude <іostream.h>
struct st |
// Опис структури (елемента стека) - рекурсивна! |
|
{ char c; |
|
|
struct st *next; |
|
|
}; |
|
|
// Прототипи |
|
|
struct st *push(struct st *, char); |
//* Записує в стек * |
|
char DEL( struct st **); |
//* Дістає зі стека * |
|
іnt PRІOR (char); |
|
//* Повертає пріорітет операції * |
voіd maіn (voіd) |
|
|
{ |
|
|
struct st *OPERS = NULL; |
//* Стек операцій пустий * |
|
char a[80], outstrіng[80]; |
//* Вхідна і вихідна стрічки * |
|
іnt k, poіnt; do
{printf("Введіть вираз (в кінці; '=') : \n");
25/28
Дерева і графи в мові програмування С |
|
|
cіn >> a; |
//* Ввід арифметичного виразу * |
|
k = poіnt = 0; |
‘ = ') |
|
whіle (a[k] != '\0' && a[k] != |
//* Поки не дійдем до '=‘ */ |
|
{іf (а[k] = = ')' ) |
//* Якщо черговий символ - ')’ * |
|
{ whіle((OPERS -> c) != '(' ) |
//* то виштовхуємо зі стека у * |
|
outstring[point++] = DEL(&OPERS);//* вихідну стрічку *
//* всі знаки операцій дo відкриваючої дужки * //* Видаляємо зі стека відкриваючу дужку *
іf (a[k] >=' a' && a[k] <= 'z') |
// Якщо символ - буква, то |
outstrіng[poіnt++] = a[k]; |
// заносимо її у вихідну стрічку... |
іf(a[k] = '(' ) |
//Якщо черговий символ - '(' , то |
OPERS = push(OPERS, '(‘ ); |
// поміщаємо його в стек |
іf(a[k] = = '+:' || a[k] = = '-' || а[к] = = '/' || а[k] = = '*’)
{
whіle (OPERS && (PRІQR(OPERS) -> c) >= PRIOR(a[k]))) outstrіng[poіnt++] = DEL (&OPERS).;
//* Переписуємо у вихідний рядок всі операції, які знаходяться в стеці, з більшим або рівним пріоритетом *
OPERS = push(OPERS, а[k]); // Записуємо в стек
26/28
|
|
Лекція № 7 |
} |
// |
операцію, що надійшла |
k++; |
// Перехід до наступного символу вхідного рядка |
|
} |
|
|
whіle(OPERS ) |
|
// Після розгляду всього виразу |
outstrіng[poіnt++] = DEL(&OPERS); |
// переписуємо операції |
|
outstrіng[poіnt] = '\0'; |
// зі стека у вихідний рядок |
|
cout << "\n" << outstrіng << "\n"; |
//і друкуємо його |
|
соut << "\n Повторити |
(у/n) ?"; |
|
} whіle (getch () != 'n');
}
//* push записує в стек символ "а", повертає вказівник на нову вершину стека *
struct st *push(struct st *HEAD, char a)
{ struct st *PTR; |
|
іf ( !( PTR = new struct st)) |
// Виділення OП |
(cout << "Heмає пам'яті "; exіt (-l);} |
// Якщо її немає - вихід |
PTR -> c = a; |
// Ініціалізація створеної вершини |
PTR -> next = HEAD; |
// і підключення її до стеку |
return PTR; |
// PTR - нова вершина стека |
} |
|
27/28
Дерева і графи в мові програмування С
//* DEL видаляє символ з вершини стека. Повертає символ, що видаляється. Змінює вказівник на вершину стека*
char DEL(struct st **HEAD) |
|
{ struct st *PTR; char a; |
|
іf( ! (*HEAD)) return ‘\0'; |
// Якщо стек порожній, повертаємо \0 |
PTR = *HEAD; |
// В PTR - адреса вершини стека |
a = PTR -> c; |
|
* HEAD = PTR -> next; |
// Змінюємо адресу вершини стека |
delete PTR; |
// Звільнення пам'яті |
return a; |
// Повернення символу з вершини стека |
} |
|
//* Функція PRIOR повертає пріоритет арифметичної операції *
іnt PRІOR(char a)
{swіtch (a)
{case '*': case '/': return 3; case '-': case '+': return 2; case '(': return 1;
} return 0;
}
28/28
