ТЕОРЕТИЧЕСКИЕ ИССЛЕДОВАНИЯ

ОТЧЕТ

По лабораторной работе № 2

 

«Калькулятор»

по дисциплине «Методы программирования»

 

Выполнил

студент гр. xxxxx/x И.И. Иванов

<подпись>

 

Проверил

В. Б. Вагисаров

<подпись>

 

Санкт-Петербург

Формулировка задания

Требуется реализовать разбор и подсчет результата скобочного арифметического выражения с числами в различных системах счисления.

Требования к программе:

- программа НЕ ИМЕЕТ меню. Она сразу после ввода принимает и немедленно обрабатывает единственный параметр (строку со скобочной последовательностью).

- После подсчета значения выражения выводится ответ в десятичном виде БЕЗ каких-либо иных слов - только результат разбора выражения ЛИБО фраза "wrong format" в том случае, если последовательность синтаксически неверная

- числа могут быть со сист. счисления до 16. Числа записываются либо как xyz..za_<какая система счисления>, либо без указания системы счисления, если число десяточное.

- гарантируется, что числа, в какой бы они ни были системе отсчета, помещаются в long int

- поддерживается 5 операций (+-*/%), приоритет повышается слева направо.

- Делать нужно через построение обратной польской записи и с реализацией стека

 

ТЕОРЕТИЧЕСКИЕ ИССЛЕДОВАНИЯ

Стек— абстрактный тип данных, представляющий собой список элементов, организованных по принципу LIFO (англ. last in — first out, «последним пришёл — первым вышел»).

Зачастую стек реализуется в виде однонаправленного списка (каждый элемент в списке содержит помимо хранимой информации в стеке указатель на следующий элемент стека).

Но также часто стек располагается в одномерном массиве с упорядоченными адресами. Такая организация стека удобна, если элемент информации занимает в памяти фиксированное количество слов, например, 1 слово. При этом отпадает необходимость хранения в элементе стека явного указателя на следующий элемент стека, что экономит память.

При организации стека в виде однонаправленного списка значением переменной стека является указатель на его вершину — адрес вершины. Если стек пуст, то значение указателя равно NULL.

Рис. 1— Организация стека в виде однонаправленного списка

Возможны три операции со стеком: добавление элемента (иначе проталкивание, push), удаление элемента (pop) и чтение головного элемента (peek).

При проталкивании (push) указывается новый элемент, указывающий на элемент, бывший до этого головой. Новый элемент теперь становится головным.

При удалении элемента (pop) убирается первый, а головным становится тот, на который был указатель у этого объекта (следующий элемент). При этом значение убранного элемента возвращается.

Преобразование выражения в ОПЗ:

  • Пока есть ещё символы для чтения:

· Читаем очередной символ.

· Если символ является числом, добавляем его к выходной строке.

· Если символ является символом функции, помещаем его в стек.

· Если символ является открывающей скобкой, помещаем его в стек.

· Если символ является закрывающей скобкой:

До тех пор, пока верхним элементом стека не станет открывающая скобка, выталкиваем элементы из стека в выходную строку. При этом открывающая скобка удаляется из стека, но в выходную строку не добавляется. Если после этого шага на вершине стека оказывается символ функции, выталкиваем его в выходную строку. Если стек закончился раньше, чем мы встретили открывающую скобку, это означает, что в выражении либо неверно поставлен разделитель, либо не согласованы скобки.

Если символ является оператором о1, тогда:

1) пока…

… (если оператор o1 право-ассоциированный) приоритет o1 меньше приоритета оператора, находящегося на вершине стека…

… (если оператор o1 ассоциированный, либо лево-ассоциированный) приоритет o1 меньше либо равен приоритету оператора, находящегося на вершине стека…

… выталкиваем верхние элементы стека в выходную строку;

2) помещаем оператор o1 в стек.

· Когда входная строка закончилась, выталкиваем все символы из стека в выходную строку. В стеке должны были остаться только символы операторов; если это не так, значит в выражении не согласованы скобки.

 

 

Результаты работы

 

Вычисление выражений происходит с помощью обратной польской записи. Наибольшие затруднения в процессе написания работы вызывал унарный минус. Число -5 по сути представляет собой выражение «0 – 5». Поэтому при встрече унарного минуса в строке мы помещаем в выходную строку 0 и 5, а в стек помещаем минус, который не выталкивает никакие предыдущие знаки операций из стека.

 

 

Рис.2. – Работа калькулятора.

 

Рис.3. – Работа калькулятора.

Рис.4. – Контроль ошибок.

ВЫВОды

Таким образом, в данной лабораторной работе был разработан простейший целочисленный калькулятор с возможностью вычисления 5 операций над числами(+-%/*). После подсчета значения выражения выводится ответ в десятичном виде без каких-либо иных слов - только результат разбора выражения либо фраза "wrong format" в том случае, если последовательность синтаксически неверная .

Алгоритм действия основан на использования обратной польской записи и стека - абстрактного типа данных, представляющего собой список элементов, организованных по принципу LIFO.


 

 

ПРИЛОЖЕНИЕ

#include <stdio.h>

#include <conio.h>

#include <locale>

#include <math.h>

#include <string.h>

#include <stdlib.h>

#define M 4096

 

typedef struct STACK{

long int data;

STACK *next;

}STACK;

 

struct STACK * push(struct STACK * top, long int x)

{

STACK *tmp = (STACK*)malloc(sizeof(STACK));

tmp->data = x;

tmp->next = top;

return(tmp);

}

 

long int pop(STACK ** top)

{

STACK *vr;

long int a;

if (*top == NULL) return '\0';

else

{

vr = *top;

a = vr->data;

*top = vr->next;

vr->next = NULL;

free(vr);

return a;

}

}

 

 

void print_s(struct STACK *top)

{

if (top == NULL) return;

else

{

long int k = 0;

char f = 0;

k = pop(&top);

f = (char)k;

putchar(f);

print_s(top);

}

}

 

void print_n(struct STACK *top)

{

if (top == NULL) return;

else

{

long int k = 0;

k = pop(&top);

printf("%i ", k);

print_n(top);

}

}

 

long int get_ch(unsigned int & p, char *str,bool &error)//Получает индекс, с которого начинается число в строке, и указатель на первый элемент в строке, переводит его в дестичную систему счисления

{

long int new_numb = 0, ct = 0, z = 0, z1 = 0;

unsigned int i = p;

int syst_check = 0;

int numb[100];

int sist = 0;

while (str[i] != '(' && str[i] != ')' && str[i] != '/' && str[i] != '*' && str[i] != '%' && str[i] != '+' && str[i] != '-' && syst_check == 0 && i < strlen(str))

{

if (str[i] == '_')

{

i++;

while (str[i] != '(' && str[i] != ')' && str[i] != '/' && str[i] != '*' && str[i] != '_' && str[i] != '%' && str[i] != '+' && str[i] != '-' && i < strlen(str))

{

sist = sist * 10 + str[i] - 48;

i++;

}

syst_check++;

}

else

{

if (str[i] == 'A' || str[i] == 'a') numb[ct] = 10;

else if (str[i] == 'B' || str[i] == 'b') numb[ct] = 11;

else if (str[i] == 'C' || str[i] == 'c') numb[ct] = 12;

else if (str[i] == 'D' || str[i] == 'd') numb[ct] = 13;

else if (str[i] == 'E' || str[i] == 'e') numb[ct] = 14;

else if (str[i] == 'F' || str[i] == 'f') numb[ct] = 15;

else numb[ct] = str[i] - 48;

i++;

ct++;

}

}

if (sist == 0) sist = 10;

if (sist > 1 && sist < 17)

{

for (int k = 0; k < ct; k++)

{

if (numb[ct - k - 1] < sist && numb[ct - k - 1] >= 0 && error==0)

{

z = pow(sist, k);

z1 = numb[ct - k - 1];

new_numb += z*z1;

}

else

{

error = 1;

printf("wrong_format\n");

p = i - 1;

break;

}

}

}

else

{

error = 1;

printf("wrong_format\n");

}

p = i - 1;

return(new_numb);

}

 

int proverka(char *str)

{

int bracket_ct = 0;

int dl = strlen(str);

char symb[5]{ '+', '*', '/', '%', '-' };//Массив символов допустимых операций.

char numb[23]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F', '_'};//Массив символов для описания чисел.

if (str[0] == '+' || str[0] == '/' || str[0] == '*' || str[0] == '%' || str[0] == ')' || (str[0] == '(' && str[1] == ')')) return(0);//Проверка первого элемента строки, так как не обрабатывается последующим алгоритмом

if (str[0] == '(') bracket_ct++;

for (unsigned int k = 1; k < dl - 1; k++)

{

int z = 0;

if (str[k] == '(')

{

for (int ct = 0; ct < 22; ct++)//Перед открывающей скобкой не должно быть числа...

if (str[k - 1] == numb[ct]) return(0);

if (str[k - 1] == ')') return(0);//...и закрывающей скобки.

for (int ct = 0; ct < 4; ct++)//После открывающей скобки не должно стоять знаков операций(кроме минуса, унарного) ...

if (str[k + 1] == symb[ct]) return(0);

if (str[k + 1] == ')') return(0);//...и закрывающей скобки.

}

else if (str[k] == ')')

{

for (int ct = 0; ct < 22; ct++)//После закрывающей скобки не должно быть числа...

if (str[k + 1] == numb[ct]) return(0);

if (str[k + 1] == '(') return(0);//...и открывающей скобки.

for (int ct = 0; ct < 5; ct++)//Перед закрывающей скобкой не должно стоять знаков операций ...

if (str[k - 1] == symb[ct]) return(0);

if (str[k - 1] == '(') return(0);//...и открывающей скобки.

}

else if (str[k] == '+' || str[k] == '-' || str[k] == '/' || str[k] == '%' || str[k] == '*')

{

for (int ct = 0; ct < 5; ct++)//Перед символом операции не должно быть еще одного символа операции...

if (str[k - 1] == symb[ct]) return(0);

for (int ct = 0; ct < 5; ct++)

if (str[k + 1] == symb[ct]) return(0);//...и после.

if (str[k - 1] == '(' && str[k] != '-') return(0);//Перед символом операции не должно быть открывающей скобки(исключение для унарного минуса) ...

if (str[k + 1] == ')') return(0);//...и закрывающей(без исключений).

}

else

{

bool prov = 0;

for (int z = 0;z<23;z++)

if(str[k] == numb[z]) prov=1;//Проверка элемента, в случае если он не является скобкой или операцией, на допустимость.

if (prov == 0)

{

return(0);

}

}

if (str[k] == '(') bracket_ct++;//Проверка правильности расположения открывающей и закрывающей скобок...

if (str[k] == ')') bracket_ct--;

if (bracket_ct < 0) return(0);//...на расположение относительно друг друга...

}

if (str[dl-1] == '+' || str[dl - 1] == '/' || str[dl - 1] == '*' || str[dl - 1] == '%' || (str[dl - 2] == '(' && str[dl - 1] == ')'))return(0);

if (str[dl-1] == '(') bracket_ct++;

if (str[dl-1] == ')') bracket_ct-=1;

if (bracket_ct != 0) return(0);//...и количество.

return(1);

}

 

long int preobr(long int oper, long int ch_1, long int ch_2)

{

long int rez = 0;

if (oper == 42) rez = ch_1 * ch_2;

else if (oper == 43) rez = ch_1 + ch_2;

else if (oper == 45) rez = ch_1 - ch_2;

else if (oper == 47) rez = ch_1 / ch_2;

else if (oper == 37) rez = ch_1 % ch_2;

return(rez);

}

 

void calc(struct STACK **symb, struct STACK **numb, bool &error)

{

long int first_ch = 0, second_ch = 0, rez = 0;

long int s = 0;

s = pop(symb);

second_ch = pop(numb);

first_ch = pop(numb);

if ((second_ch == 0 && (s == 47 || s == 37)) || ((first_ch < 0 || second_ch < 0) && s == 37 ))

{

printf("wrong_format\n");

error = 1;

}

else

{

rez = preobr(s, first_ch, second_ch);

*numb = push(*numb, rez);

}

return;

}

 

void main(void)

{

while (1)

{

bool error = 0;

unsigned int i = 0;

struct STACK *symb = NULL;//стек операторов

struct STACK *numb = NULL;//стек чисел

setlocale(LC_ALL, "Russian");

char str[M];

gets_s(str);

if (proverka(str) == 0)//проверка строки на правильность

{

printf("wrong_format\n");

}

else//если пройдена проверка, запуск вычислений

{

for (i = 0; i < strlen(str); i++)

{

if (symb == NULL && (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/' || str[i] == '%'))

{

symb = push(symb, str[i]);

}

else if (error == 1) break;

else if (str[i] == '(') symb = push(symb, str[i]);

else if (str[i] == ')')

{

long int k = 0;

while (symb->data != 40)

{

calc(&symb, &numb, error);

}

k = pop(&symb);

}

else if (str[i] == '*' || str[i] == '/' || str[i] == '%')

{

long int k = symb->data;

if (k == 42 || k == 47 || k == 37)

{

calc(&symb, &numb, error);

symb = push(symb, str[i]);

}

else symb = push(symb, str[i]);

}

else if (str[i] == '+' || str[i] == '-')

{

if (str[i] == '-' && ((i > 0 && str[i - 1] == '(') || i == 0))

{

numb = push(numb, 0);

symb = push(symb, str[i]);

}

else

{

while (symb != NULL && symb->data != 40)

{

calc(&symb, &numb, error);

}

symb = push(symb, str[i]);

}

}

else if (str[i] == '_')

{

printf("wrong_format\n");

error=1;

break;

}

else numb = push(numb, get_ch(i, str,error));

}

if (error != 1)

{

while (symb != NULL && error != 1)

calc(&symb, &numb, error);

if (error != 1)

{

long int rezult = 0;

rezult = pop(&numb);

printf("%i\n", rezult);

free(numb);

free(symb);

}

}

}

_getch();

}

}