Пример: Шаблонный класс для вычисления значения выражения, вводимого с клавиатуры
Нижеследующая программа является консольным приложением Windows.
#define _USE_MATH_DEFINES
#include <iostream>
#include <conio.h>
#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <cmath>
using namespace std;
#define NUMVARS 26 // размер массива для хранения значений переменных
template <class PType> class CParser //анализатор
{
//типы элементов выражения:
enum types {DELIMITER = 1, VARIABLE, NUMBER};
//разделитель, переменная, число
// элементы данных класса:
char* exp_ptr; // Указатель на выражение
char token[80]; // Элемент выражения
char tok_type; // Тип элемента
//функции:
int isdelim(char c); // проверяет, является ли элемент разделителем
void get_token(); /* выбирает из строки очередной элемент выражения, помещает его в строку token и определяет его тип */
void putback();//возврат в выражении на 1 элемент назад:
void eval_exp1(PType &result); // присваивание
void eval_exp2(PType &result); // Сложение и вычитание
void eval_exp3(PType &result); // Умножение, деление, остаток от деления
void eval_exp4(PType &result); // Возведение в степень
void eval_exp5(PType &result); // Унарный плюс или минус
void eval_exp6(PType &result); // Обработка выражения в скобках
void atom (PType &result); // Значение числа
void serror(int error); // вывод сообщения об ошибке
PType find_var(char* s); // возвращает значение переменной
public:
CParser(); // конструктор
PType vars[NUMVARS]; // массив значений переменных
PType eval_exp(char *exp); //точка входа
} ;
//определение функций:
// записываем нули в массив значений переменных:
template <class PType> CParser<PType>::
CParser()
{
exp_ptr = NULL;
for (int i=0; i<NUMVARS; i++)
vars[i]=0;
}
// Функция, проверяющая, является ли элемент разделителем:
template <class PType> int CParser<PType>::
isdelim(char c)
{
if (strchr(" +-/*%^=()",c) || c==9 || c=='\r' || c==0)
return 1;
else return 0;
}
//Функция, которая выбирает из выражения очередной элемент
template <class PType> void CParser<PType>::
get_token()
{
register char *temp = token; //Указатель на token
*temp='\0';
tok_type=0;
if (! *exp_ptr) return; //Конец выражения
while (isspace(*exp_ptr)) ++exp_ptr; // Пропускаем пробелы
if (strchr("+-*/%^=()", *exp_ptr)) // Если очередной символ - знак операции
{
tok_type=DELIMITER;
*temp++ = *exp_ptr++; //Продвигаемся к след. символу
}
else if (isalpha(*exp_ptr))
{
tok_type=VARIABLE;
while (!isdelim(*exp_ptr)) *temp++ = *exp_ptr++;
while (isspace(*exp_ptr)) ++exp_ptr;
//if (*exp_ptr == '(') tok_type=FUNCTION;
}
else if (isdigit(*exp_ptr))
{
tok_type = NUMBER;
while (!isdelim(*exp_ptr)) *temp++ = *exp_ptr++;
}
*temp = '\0';
}
// точка входа в программу разбора выражения:
template <class PType> PType CParser<PType>::
eval_exp(char *exp)
{
PType result;
exp_ptr = exp;
get_token(); // 1 - ый операнд
if (! *token)
{
serror(2); //Нет выражения
return 0;
}
eval_exp1(result);
if(*token) serror(0);
return result;
}
//возврат в выражении на 1 элемент назад:
template <class PType> void CParser<PType>::
putback()
{
char *t = token;
for (; *t; t++) exp_ptr--;
}
//присваивание:
template <class PType> void CParser<PType>::
eval_exp1(PType &result)
{
int slot;
char ttok_type;
char temp_token[80];
if (tok_type == VARIABLE)
{
//сохранить старый элемент:
strcpy(temp_token, token);
ttok_type = tok_type;
//вычисление индекса переменной:
slot = toupper(*token)-'A'; get_token();
if (*token != '=')
{
putback(); //вернуть текущий элемент
//восстанавливаем предыдущий элемент
strcpy(token, temp_token);
tok_type = ttok_type;
}
else
{
get_token();
eval_exp2(result);
vars[slot] = result;
return;
}
}
eval_exp2(result);
}
// Сложение и вычитание:
template <class PType> void CParser<PType>::
eval_exp2(PType &result)
{
register char op;
PType temp;
eval_exp3(result);
while ( ( op = *token ) == '+' || op == '-')
{
get_token();
eval_exp3(temp);
switch (op)
{
case '+':
result+=temp;
break;
case '-':
result-=temp;
break;
}
}
}
// Умножение, деление, остаток от деления:
template <class PType> void CParser<PType>::
eval_exp3(PType &result)
{
register char op;
PType temp;
eval_exp4(result);
while ( ( op = *token) == '*' || op == '/' || op == '%')
{
get_token();
eval_exp4(temp);
switch (op)
{
case '*':
result*=temp;
break;
case '/':
result/=temp;
break;
case '%':
result=(int)result % (int) temp;
break;
}
}
}
// Возведение в степень:
template <class PType>void CParser<PType>::
eval_exp4(PType &result)
{
PType temp;
eval_exp5(result);
if ( *token == '^')
{
get_token();
eval_exp4(temp);
result=pow(result,temp);
}
}
// Унарный плюс или минус:
template <class PType>void CParser<PType>::
eval_exp5(PType &result)
{
register char op;
op=0;
if ((tok_type == DELIMITER) && *token == '+'
|| *token == '-')
{
op= *token;
get_token();
}
eval_exp6(result);
if (op == '-') result = -result;
}
// Обработка выражения в скобках:
template <class PType>void CParser<PType>::
eval_exp6(PType &result)
{
if ( *token == '(' )
{
get_token();
eval_exp1(result); // вычисляем выражение в скобках
if (*token != ')')
serror(1);
get_token();
}
else atom(result);
}
// Значение числа:
template <class PType>void CParser<PType>::
atom (PType &result)
{
switch (tok_type)
{
case NUMBER:
result = atof(token);
get_token(); return;
case VARIABLE:
result = find_var(token);
get_token(); return;
default: serror(0);
}
}
// вывод сообщения об ошибке:
template <class PType>void CParser<PType>::
serror(int error)
{
static char *e[]=
{
"Синтаксическая ошибка",
"Незакрытые скобки",
"Выражение записано неверно"
};
cout << e[error] << endl;
}
// возвращает значение переменной:
template <class PType>PType CParser<PType>::
find_var(char* s)
{
if (!isalpha(*s))
{
serror(1);
return 0;
}
int i=toupper(*token) - 'A';
return vars[i];
}
// файл “Calculator.cpp” :
/*#include <iostream>
#include <conio.h>
#include <windows.h>*/
#include "calculator.h"
//using namespace std;
int main()
{
//Настройки шрифтов и региональных стандартов:
if(SetConsoleCP(1251)==0)
//проверка правильности установки кодировки символов для ввода
{
cerr<<"Fialed to set codepage!"<<endl;
/* если не удалось установить кодовую страницу, вывод сообщения об ошибке */
}
if(SetConsoleOutputCP(1251)==0)//тоже самое для вывода
{
cerr<<"Failed to set OUTPUT page!"<<endl;
}
CParser <double> ob;
char expstr[81];
cout<<" Для выхода из программы введите пустое выражение (нажмите <Enter>)\n";
while(1)
{
cout<<"Введите выражение ";
cin.getline(expstr, 80);
if (expstr[0] == 0) break;
cout<<"Результат = ";
cout<<ob.eval_exp(expstr) << endl;
}
_getch();
return 0;
}
Запустите программу (<Ctrl>+<F5>) и введите несколько выражений, например:
x = -2.5
3 * (x – 1.5^2)
Убедитесь в правильности работы программы.