Форум программистов, компьютерный форум CyberForum.ru

Написать строковый калькулятор - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 9, средняя оценка - 4.67
newbiee
0 / 0 / 0
Регистрация: 14.04.2013
Сообщений: 5
14.04.2013, 00:51     Написать строковый калькулятор #1
здравствуйте. просьба, хотя бы идейно, а лучше с примером подсказать, как научить программу решать пример.
суть: 5 + 5 - 28 * 3. нажимаю enter, выдаёт ответ. запускаю заново, пишу: 89 - 64 + 3, нажимаю enter, выдаётся ответ. а в идеале и такое: 2^3 - 5^(2/3).
подскажите, пожалуйста.
Лучшие ответы (1)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Simpson_
15 / 15 / 3
Регистрация: 06.02.2013
Сообщений: 52
14.04.2013, 01:11     Написать строковый калькулятор #2
Через стек.
Записать исходный пример в массив. Перевести в постфиксную форму, и оценить постфиксное фыражение.

Вот пример но он только для целых чисел и без проверки на корректность ввода.


C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
//РАБОТАЕТ ТОЛЬКО С ЦЕЛЫМИ ЧИСЛАМИ
 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
 
struct stackNode                                //для перевода в постфиксную форму
   {
    char data;
    struct stackNode* nextPtr;
   };
 
struct numberStackNode                          //стэк для калькулятора
   {
    int data;
    struct numberStackNode* nextPtr;
   };
 
typedef struct stackNode StackNode;                //для перевода в постфиксную форму
typedef StackNode* StackNodePtr;
 
typedef struct numberStackNode NumberStackNode;    //для калькулятора
typedef NumberStackNode* NumberStackNodePtr;
 
 
void convertToPostfix(char*, char*);          //переводит выражение и инфиксной в постфиксную запись
int isOperator(char);                         //определяет, является ли символ оператором
int precedence(char, char);                   //сравнивает по приоритету
 
void push(StackNodePtr*, char);
void pushInt(NumberStackNodePtr*, int);              //добавляет в стэк
 
char pop(StackNodePtr*);
int popInt(NumberStackNodePtr*);                      //выбирает из стэка
 
char stackTop(StackNodePtr);                  //смотрит верхнее значение стэка
int isEmpty(StackNodePtr);                    //проверяет пуст ли стэк
void printStack(NumberStackNodePtr);                //вывоит стэк(не используется)
int evaluatePostfixExpression(char*);             //создаёт элементарные выражения для calculate()
int calculate(int, int, char);                    //считает элементарные выражения
 
int main(void)
{
   char string[100];
   char postString[100];
   int i;
 
   printf("Enter infix string: ");
   gets(string);
   convertToPostfix(string, postString);
   printf("Postfix string: ");
   puts(postString);
   printf("Rezult: %d\n", evaluatePostfixExpression(postString));
 
   system("pause");
   return 0;
}
 
void push(StackNodePtr* topPtr, char value)
{
   StackNodePtr newPtr;
 
   newPtr=malloc(sizeof(StackNode));
 
   if(newPtr!=NULL)
     {
      newPtr->data=value;
      newPtr->nextPtr=*topPtr;
      *topPtr=newPtr;
     }
   else
      printf("%c not inserted. No memory available.\n", value);
}
 
void pushInt(NumberStackNodePtr* topPtr, int value)
{
   NumberStackNodePtr newPtr;
 
   newPtr=malloc(sizeof(NumberStackNode));
 
   if(newPtr!=NULL)
     {
      newPtr->data=value;
      newPtr->nextPtr=*topPtr;
      *topPtr=newPtr;
     }
   else
      printf("%d not inserted. No memory available.\n", value);
}
 
char pop(StackNodePtr* topPtr)
{
   StackNodePtr tempPtr;
   char popValue;
 
   tempPtr=*topPtr;
   popValue=(*topPtr)->data;
   *topPtr=(*topPtr)->nextPtr;
  // printf("%c", tempPtr->data);
   free(tempPtr);
 //  printf("\n");
   return popValue;
}
 
 
int popInt(NumberStackNodePtr* topPtr)
{
   NumberStackNodePtr tempPtr;
   int popValue;                                  
 
   tempPtr=*topPtr;
   popValue=(*topPtr)->data;
   *topPtr=(*topPtr)->nextPtr;
  // printf("%c", tempPtr->data);
   free(tempPtr);
 //  printf("\n");
   return popValue;
}
 
void printStack(NumberStackNodePtr currentPtr)
{
   if(currentPtr==NULL)
      printf("The stack is empty.\n\n");
   else
     {
     printf("The stack is:\n");
 
      while(currentPtr!=NULL)
         {
          printf("%d --> ", currentPtr->data);
          currentPtr=currentPtr->nextPtr;
         }
 
      printf("NULL\n\n");
     }
}
 
int isEmpty(StackNodePtr topPtr)
{
   return topPtr==NULL;
}
 
char stackTop(StackNodePtr topPtr)
{
  return topPtr->data;
}
 
int precedence(char operator1, char operator2)                //ничё другого не пришло в голову...
{
   char oper[]={'+', '-', '*', '/', '^', '%'};
   int prior[]={0, 0, 1, 1, 2, 2};
   int counter=-1, op1, op2;
 
   while(++counter<6)
      {
       if(oper[counter]==operator1)
          op1=prior[counter];
 
       if(oper[counter]==operator2)
          op2=prior[counter];
      }
 
   if(op2==op1)
      return 0;
   else if(op1<op2)
      return -1;
   else if(op1>op2)
      return 1;
 
}
 
int isOperator(char c)
{
   if(c=='+' || c=='-' || c=='*' || c=='/' || c=='^' || c=='%')
      return 1;
 
   return 0;
}
 
void convertToPostfix(char* infix, char* postfix)
{
   StackNodePtr stackPtr=NULL;
   int len, i=0, j=0;
   char temp;
 
   push(&stackPtr, '(');
   len=strlen(infix);
   *(infix+len)=')';
   *(infix+len+1)='\0';
 
   while(stackPtr!=NULL)
      {
       if(isdigit(*(infix+i)))
         {
          *(postfix+j++)=*(infix+i++);
 
          while(isdigit(*(infix+i)))
             *(postfix+j++)=*(infix+i++);
 
          *(postfix+j++)=' ';
         }                                     
       else if(*(infix+i)=='(')
         {
          push(&stackPtr, '(');
          i++;
         }
       else if(isOperator(*(infix+i)))
         {
          while(isOperator(stackPtr->data) && precedence(stackPtr->data, *(infix+i))!=-1)
             {
              *(postfix+j++)=pop(&stackPtr);
              *(postfix+j++)=' ';
             }
 
          push(&stackPtr, *(infix+i++));
         }
       else if(*(infix+i)==')')
         {
          while(stackPtr->data!='(')
             {
              *(postfix+j++)=pop(&stackPtr);
              *(postfix+j++)=' ';
             }
 
          temp=pop(&stackPtr);
          i++;
         }
      }
 
   *(postfix+j)='\0';
}
 
int evaluatePostfixExpression(char *postfixString)
{
   NumberStackNodePtr stackPtr=NULL;
   int x=0, y=0;
   int i=0;
   char *resPtr;
 
   while(*(postfixString+i)!='\0')
      {
       if(*(postfixString+i)==' ')
         {
          i++;
          continue;
         }
 
       if(isdigit(*(postfixString+i)))
         {
          pushInt(&stackPtr, strtol(postfixString, &resPtr, 0));
 
          if(resPtr[0]==' ')
             postfixString=&resPtr[1];
         else
             postfixString=resPtr;
         }
       else
         {
          y=popInt(&stackPtr);
          x=popInt(&stackPtr);
          printf("x=%d %c y=%d =%d\n", x, *(postfixString+i),y, calculate(x, y, *(postfixString+i)));
          pushInt(&stackPtr, calculate(x, y, *(postfixString+i)));
          postfixString+=2;
         }
      }
 
   return popInt(&stackPtr);
}
 
int calculate(int x, int y, char opr)
{
   int rez=0;
 
   switch(opr)
      {
       case '+':
          rez=x+y;
          break;
       case '-':
          rez=x-y;
          break;
       case '*':
          rez=x*y;
          break;
       case '/':
          if(y==0)
          rez=0;
          else
          rez=x/y;
          break;
       case '^':
          rez=pow(y, x);
          break;
       case '%':
          rez=x%y;
       default:
          break;
      }
 
   return rez;
}
Rifle
 Аватар для Rifle
25 / 25 / 5
Регистрация: 15.11.2012
Сообщений: 93
Завершенные тесты: 4
14.04.2013, 01:26     Написать строковый калькулятор #3
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Вот готовый пример калькулятора, не используя стек, а просто обрабатывая строку.
Сложение, вычитание, умножение, деление, возведение в степень, скобочки, тригонометрические функции (sin, cos, tang). При желании можно добавить еще что-нибудь.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include <iostream>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cmath>
using std::cin;
using std::cout;
using std::endl;
#include "Windows.h"
 
void eatspaces(char* str); //функция для удаления пробелов
double expr(char* str); //функция, вычисляющая выражение
double term(char* str, int& index); //функция для анализа элемента
double trigon(char* str, int& index); //функция для вычисления тригонометрических значений
double power(char* str, int& index); //функция для извлечения степени
double number(char* str, int& index); //функция, распознающая число
char* extract(char* str, int& index); //функция для извлечения подстроки
 
const int MAX = 80; //максимальный размер буфера
 
int main()
    {
    setlocale(0, "Russian");
    char buffer[MAX] = {0}; //область хранения вычисляемого входного выражения
    cout << endl << "Добро пожаловать в дружественный колькулятор!" << endl
         << "Введите выражение или пустую строку для завершения." << endl;
    for ( ; ; )
        {
        cin.getline(buffer, sizeof buffer); //читаем входную строку
        eatspaces(buffer); //удаляем все пробелы из строки
        if (!buffer[0]) //если пустая строка
            return 0;
        cout << "= " << expr(buffer)
             << endl << endl;
        }
    }
        
void eatspaces(char* str)
    {
    int i = 0;                          //индекс места в строке "куда копировать"
    int j = 0;                          //индекс места в строке "откуда копировать"
    while ((*(str+i) = *(str + j++)) != '\0') //цикл, пока очередной символ не '\0'
        if (*(str+i) != ' ')  //увеличиваем i, если символ не пробел
            i++;
    return;
    }
    
double expr(char* str)
    {
    double value = 0.0;         //здесь сохраняем результат
    int index = 0;              //текущая позиция символа
    value = term(str, index);   //получить первый элемент
    for ( ; ; )                 //бесконечный цикл, выход внутри
        {
        switch (*(str+index++)) //выбрать действие на основе текущего символа
            {
            case '\0':          //конец строки, возвращаем значение
                return value;
            case '+':           //знак плюс, прибавляем элемент к value
                value += term(str, index);
                break;
            case '-':           //знак минус, вычитаем элемент из value
                value -= term(str, index);
                break;
            default:            //все остальное не котируется
                int i = index;
                while (--i > 0)
                    cout << " ";
                cout << "^" << endl;
                cout << "Эй, повнимательней можно?! Здесь обнаружена ошибка. " << endl;
                exit(1);
            }
        }
    }
    
double term(char* str, int& index)
    {
    double value = 0.0;             //здесь накапливается значение результата
    value = power(str, index);      //получить первое число элемента
    //выполняем цикл до тех пор, пока имеем допустимую операцию
    while ((*(str + index) == '*') || (*(str + index) == '/')) 
        {
        if (*(str+index) == '*')
            value *= power(str, ++index);
        if (*(str+index) == '/')
            value /= power(str, ++index);
        }
    return value;
    }
    
    
double power(char* str, int& index)
    {
    double value = 0.0;
    value = trigon(str, index);
    while (*(str + index) == '^')
        {
        value = pow(value, trigon(str, ++index)); //возводим в степень
        }
    return value;
    }   
 
double trigon(char* str, int& index)
    {
    int buf_index = 0;
    int temp_index = index; //переменная для хранения индекса (чтобы если что вернуть индекс без изменений)
    char* p_str = 0;    //временный указатель для сравнения символов
    double value = 0;
    while (isalpha(*(str + temp_index)))
        {
        buf_index++;    //сколько букв
        temp_index++;   //текущий индекс
        }
    if (!buf_index)     //если нет ни одной буквы, то возвращаем число
        {
        value = number(str, index);
        return value;
        }
    else                //иначе смотрим, являются ли буквы чем-нибудь этим
        {
        p_str = new char[buf_index+1];  //а для этого создаем временную строку, чтобы сравнить
        p_str[buf_index] = '\0';
        strncpy(p_str, str+index, buf_index);
        }
    if (strcmp(p_str, "sin") == 0)      //синус в градусах
        {
        value = sin(3.141592/180*number(str, temp_index));
        index = temp_index;
        delete[] p_str;     //не забываем удалить временную строку
        return value;
        }
    else if (strcmp(p_str, "cos") == 0) //косинус в градусах
        {
        value = cos(3.141592/180*number(str, temp_index));
        index = temp_index;
        delete[] p_str;     //не забываем удалить временную строку
        return value;
        }
    else if (strcmp(p_str, "tan") == 0) //тангенс в градусах
        {
        value = tan(3.141592/180*number(str, temp_index));
        index = temp_index;
        delete[] p_str;     //не забываем удалить временную строку
        return value;
        }
    else
        {
        return value;
        }
    }
    
double number(char* str, int& index)
    {
    double value = 0.0;                 //хранит результирующее значение
    if (*(str + index) == '(')
        {
        char* p_substr = 0;
        p_substr = extract(str, ++index);
        value = expr(p_substr);
        delete[] p_substr;
        return value;
        }
    //продуманский цикл, превращает символы в число
    while (isdigit(*(str+index)))       //цикл накапливает ведущие цифры 
        value = 10*value + (*(str + index++) - '0');
    if (*(str + index) != '.')          //если не цифра, проверяем на десятичную точку
        return value;
    double factor = 1.0;                //множитель для десятичных разрядов
    //еще один продуманский цикл, возвращает десятичную часть
    while (isdigit(*(str + (++index)))) //выполнять цикл, пока идут цифры 
        {
        factor *= 0.1;
        value = value + (*(str + index) - '0')* factor;
        }
    return value;
    }
    
char* extract(char* str, int& index)
    {
    char buffer[MAX];       //временное пространство для подстроки
    char* p_str = 0;        //указатель на новую строку для возврата
    int numL = 0;           //счетчик найденных левых скобок
    int buf_index = index;  //сохранить начальное значение index
    do
        {
        buffer[index - buf_index] = *(str+index); //копируем символ текущей строки в подстроку
        switch(buffer[index - buf_index]) //смотрим, чо это за символ
            {
            case ')':
                if (numL == 0)
                    {
                    buffer[index - buf_index] = '\0'; //если счетчик скобочек верный, ставим символ конца строки
                    ++index;    //устанавливаем индекс на следующий за скобочкой элемент
                    p_str = new char[index-buf_index];
                    if (!p_str)
                        {
                        cout << "Выделение памяти не удалось, программа прервана.";
                        exit(1);
                        }
                    strcpy_s(p_str, index - buf_index, buffer); //и копируем подстроку в новую память
                    return p_str;
                    }
                else
                    numL--;     //уменьшаем счетчик скобок
                break;
            case '(':
                numL++;         //соответственно увеличиваем
                break;
            }
        } while (*(str + index++) != '\0');     //устанавливаем индекс в следующий элемент
    cout << "Вывод за пределы выражения, возможно, плохой ввод." << endl;
    exit(1);
    return p_str;
    }
cygwin
1 / 1 / 0
Регистрация: 10.04.2013
Сообщений: 17
14.04.2013, 02:33     Написать строковый калькулятор #4
Вместо тысячи слов...
http://ru.wikipedia.org/wiki/%D0%9E%...B8%D1%81%D1%8C
AnyOne697
 Аватар для AnyOne697
134 / 106 / 5
Регистрация: 22.05.2010
Сообщений: 532
14.04.2013, 03:11     Написать строковый калькулятор #5
Rifle и _Simpson правильно говорят.
Разве только я бы делал через дерево или очередь.
Почему? Потому что так удобнее держать единственное результирующее значение, а юзая рекурсию вообще получаем едва ли не бесплатный калькулятор. Правда стек и реализует рекурсию более эффектино, придётся немного подумать. Да и очередь почти одно и тоже даст. А так, я вижу примерно такое:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include ...
 
using ...
 
enum Operator ( sum, substract, mult, div );
 
int count ( string expression, Operator op = sum ) { // op is 'operator'
    stringList splits = expression.split ( op )
    if ( stringList.count == 1 ) {
        return parseInt ( stringList[ 0 ] );
    }
    int res = ( op == sum || op == substract ? 0 : 1 );
    foreach ( string &s in splits ) {
        if ( op == sum || op == substract ) {
            res += ( op == substract ? -1 : 1 ) * count ( s );
        } else if ( op == mult ) {
            res *= count ( s );
        } else if ( op == div ) {
            res /= count ( s );
        }
    }
    
    return res;
}
 
int main ( int argc, char **argv ) {
    println ( "%d\n", count ( argv[ 1 ] ) );
    
    return 0;
}
Внимание! Компилировать никак не получиться, да и вообще стиль написания кода соответствует стилю написания кода в 2 часа ночи в перерывах между ходами в HoMM4 и почитывания хабры, так что "казнить нельзя, помиловать".
Алсо, здесь потребуется много чего реализовать. То как доделать класс std::string, сделать класс StringList и так далее. Да и использовались плюшки из C++11.

Добавлено через 37 минут
Update:
Это рекурсия, но по сути она соответствует дереву. Работает через стек, конечно. Производительность пониже, но писать намного проще.

Добавлено через 1 минуту
Цитата Сообщение от cygwin Посмотреть сообщение
Вместо тысячи слов...
http://ru.wikipedia.org/wiki/%D0%9E%...B8%D1%81%D1%8C
Не, это не зергут. На wolframalpha.com так не издеваются, например.
asdds
Сообщений: n/a
14.04.2013, 07:55     Написать строковый калькулятор #6
спасиб как раз то что на прогу надо ьыло
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
14.04.2013, 12:17     Написать строковый калькулятор
Еще ссылки по теме:

Написать калькулятор, работающий с дробями C++
C++ Написать простейший калькулятор
C++ Калькулятор выражений(строковый) через бинарное дерево

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
newbiee
0 / 0 / 0
Регистрация: 14.04.2013
Сообщений: 5
14.04.2013, 12:17  [ТС]     Написать строковый калькулятор #7
всем спасибо, сижу разбираюсь)
Yandex
Объявления
14.04.2013, 12:17     Написать строковый калькулятор
Ответ Создать тему
Опции темы

Текущее время: 19:24. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru