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

Калькулятор: обратная польская запись - C++

Восстановить пароль Регистрация
 
Gr1f0nn
82 / 81 / 42
Регистрация: 30.09.2012
Сообщений: 408
30.06.2015, 22:07     Калькулятор: обратная польская запись #1
Всем доброго времени суток!
Решил написать для себя калькулятор, который бы решал сложные выражения с учетом скобок, каких-либо функций и т.д. В дальнейшем хотел прикрутить свой класс больших чисел, но до этого пока не дошел, так как наткнулся на ошибку, которая возникает при обработке скобок. Самое интересно, что в некоторых случаях, все работает корректно.
Важно: если будете тестировать, то учитывайте тот факт, что пока что обрабатываются только однозначные числа (или просто цифры, кому как удобней ^_^)

Код:
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
#include <iostream>
#include <stack>
#include <vector>
#include <iterator>
#include <algorithm>
#include <math.h>
bool is_symbol(char sym)
{
    switch(sym)
    {
        case '+': return true;
        case '-': return true;
        case '(': return true;
        case '^': return true;
        // ---------- //
        case '/': return true;
        case '*': return true;
    }
    return false;
}
 
bool is_mid_priority(char sym)
{
    switch(sym)
    {
        case '/': return true;
        case '*': return true;
    }
    return false;
}
 
bool is_low_priority(char sym)
{
    switch(sym)
    {
        case '-': return true;
        case '+': return true;
    }
    return false;
}
 
bool is_high_priority(char sym)
{
    return sym == '^' ? true : false;
}
 
int main()
{
    //std::string expression("(3 + 4 - 5) * 2"); // ??????
    //std::string expression("1 + (5 - 3) * (3 - 1) + 2");
    //std::string expression("2 ^ 3 * 2 + 1");
    //std::string expression("2 + 3 + 5 * 2 + 3 * 6 / 2");
    std::string expression("2 + 3 + 5 * (2 + 3 * 6) / 2"); // ???
    std::stack<char> symbols;
    std::vector<char> exit_string;
    std::stack<double> result;
    double buffer = 0;
 
    for(std::string::iterator it = expression.begin(); it != expression.end(); ++it)
    {
        if(::isdigit(*it)) exit_string.push_back(*it); // Если это цифра
        else if(is_symbol(*it)) // Если это символ
        {
            if(is_low_priority(*it))
            {
                if(symbols.size() && !is_low_priority(symbols.top()))
                {
                    exit_string.push_back(symbols.top());
                    symbols.pop();
                }
                symbols.push(*it);
            }
            else if(is_mid_priority(*it))
            {
                if(symbols.size() && is_high_priority(symbols.top()))
                {
                    exit_string.push_back(symbols.top());
                    symbols.pop();
                }
                symbols.push(*it);
            }
            else if(is_high_priority(*it))
            {
                symbols.push(*it);
            }
            else symbols.push(*it);
        }
        else if(*it == ')')
        {
            while(symbols.top() != '(')
            {
                std::cout << "\n1";
                exit_string.push_back(symbols.top());
                symbols.pop();
            }
            if(symbols.size()) { symbols.pop(); }
        }
    }
    while(symbols.size()) { exit_string.push_back(symbols.top()); symbols.pop(); }
    std::copy(exit_string.begin(), exit_string.end(), std::ostream_iterator<char>(std::cout, " "));
 
    for(std::vector<char>::iterator it = exit_string.begin(); it != exit_string.end(); ++it)
    {
        if(::isdigit(*it)) result.push(*it - '0');
        else
            switch(*it)
            {
            case '+': { buffer = result.top(); result.pop(); result.top() += buffer; break; }
            case '-': { buffer = result.top(); result.pop(); result.top() -= buffer; break; }
            case '*': { buffer = result.top(); result.pop(); result.top() *= buffer; break; }
            case '/': { buffer = result.top(); result.pop(); result.top() /= buffer; break; }
            case '^': { buffer = result.top(); result.pop(); result.top() = pow(result.top(), buffer); break; }
            }
    }
    std::cout << "\nANSWER: " << result.top();
    return 0;
}
Собственно, те выражения, которые помечены вопросами, и вызывают крах программы. По моим наблюдениям, крах происходит в цикле, который обрабатывает вхождение закрывающей скобки.
Также, если кто-то что-то подскажет по поводу улучшения реализации, да и вообще любые другие замечания по коду в целом, то также буду очень благодарен! =)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
30.06.2015, 22:07     Калькулятор: обратная польская запись
Посмотрите здесь:

Вычисление выражения, использование обратной польской записи C++
C++ Обратная польская запись (ОПЗ) на С++
Обратная польская запись C++
Обратная польская запись C++
Обратная польская запись C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
gazlan
2855 / 1803 / 271
Регистрация: 27.08.2010
Сообщений: 4,883
Записей в блоге: 1
30.06.2015, 22:24     Калькулятор: обратная польская запись #2
Цитата Сообщение от Gr1f0nn Посмотреть сообщение
#include <iostream> #include <stack> #include <vector> #include <iterator> #include <algorithm>

Не по теме:

Осподи!


http://stjarnhimlen.se/snippets/eval.c

Суперкалькулятор
Gr1f0nn
82 / 81 / 42
Регистрация: 30.09.2012
Сообщений: 408
30.06.2015, 22:37  [ТС]     Калькулятор: обратная польская запись #3

Не по теме:

Цитата Сообщение от gazlan Посмотреть сообщение
Осподи!
Есть такое ^_^
Много лишнего наворочено, но пока решил сделать первую рабочую версию, а потом уже приводить все в порядок (касаемо применения стандартных алгоритмов, итераторов, векторов и т.д и т.п.).



За ссылки спасибо, попробую разобраться в решениях, но, как мне кажется, это будет несколько неправильный подход (???), так как, в итоге, обработка строки скорее всего будет скопирована, а ошибку я так и не найду ^_^

Добавлено через 5 минут
Есть идея сделать что-то вроде счетчика, который бы подсчитывал количество операций внутри каждой скобки, но это слабо похоже на адекватное решение. Просто, не смотря на проверки не пустоты стека, как я понял, программа все равно либо выходит за его границы, либо это как раз "непредсказуемый" вывод результата команд .pop() и .top(), если стек пуст.
gazlan
2855 / 1803 / 271
Регистрация: 27.08.2010
Сообщений: 4,883
Записей в блоге: 1
30.06.2015, 22:51     Калькулятор: обратная польская запись #4
Классика: Four Function Calculator: An AnaGram Example

+

Страуструп. Грамматики. Парсер - C++
Упражнение для программы калькулятор из книги Язык программирования С++

+ поиск по форуму.
Gr1f0nn
82 / 81 / 42
Регистрация: 30.09.2012
Сообщений: 408
30.06.2015, 23:07  [ТС]     Калькулятор: обратная польская запись #5
Проблему нашел. Почему-то в некоторых случаях открывающая скобка не записывается в стек.

Добавлено через 13 минут
Все. Проблема решена.
Ошибка была в этой строке:
C++
1
if(symbols.size() && !is_low_priority(symbols.top()))
Открывающая скобка в данном случае также проходила по условию, поэтому она выходила из стека раньше времени.
Решил так:
C++
1
if(symbols.size() && (is_mid_priority(symbols.top()) || is_high_priority(symbols.top())))
Здесь скобка точно не пройдет ^_^
Yandex
Объявления
30.06.2015, 23:07     Калькулятор: обратная польская запись
Ответ Создать тему
Опции темы

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