Kiberchief
@Kiberchief
Пока учусь.

Как написать простой калькулятор?

Хочу написать однострочный калькулятор, но при написании возникла проблема: функция atoi(ex.c_str()) дублирует первый введенный параметр. Подскажите, как это пофиксить и по возможности дайте совет по логике, а то куча ифов меня напрягает.

int fu(std::string ex)
{
    std::string r;
    float result=0;
    for (size_t v=0;v<ex.size();++v)
    {
        r=ex[4];
        if (ex[v]=='+')result=atoi(ex.c_str())+atoi(ex.c_str());
        else if (ex[v]=='+' || ex[v]=='*')result=atoi(ex.c_str())+(atoi(ex.c_str())*atoi(r.c_str()));
        else if (ex[v]=='+' || ex[v]=='/')result=atoi(ex.c_str())+(atoi(ex.c_str())/atoi(r.c_str()));

        else if (ex[v]=='-')result=atoi(ex.c_str())-atoi(ex.c_str());
        else if (ex[v]=='-' || ex[v]=='*')result=atoi(ex.c_str())-atoi(ex.c_str())*atoi(r.c_str());
        else if (ex[v]=='-' || ex[v]=='/')result=atoi(ex.c_str())-atoi(ex.c_str())/atoi(r.c_str());

        else if (ex[v]=='*')result=atoi(ex.c_str())*atoi(ex.c_str());
        else if (ex[v]=='*' || ex[v]=='-')result=atoi(ex.c_str())*atoi(ex.c_str())-atoi(r.c_str());
        else if (ex[v]=='*' || ex[v]=='+')result=atoi(ex.c_str())*atoi(ex.c_str())+atoi(r.c_str());
        else if (ex[v]=='*' || ex[v]=='/')result=atoi(ex.c_str())*(atoi(ex.c_str())/atoi(r.c_str()));

        else if (ex[v]=='/')result=atoi(ex.c_str())/atoi(ex.c_str());
    }
    return result;
}
  • Вопрос задан
  • 222 просмотра
Решения вопроса 1
@majstar_Zubr
C++, C#, gamedev
Я отвечу для случая работы с простым форматом ввода:
12345 + 6789

1) вы не модифицируете входную строку, поэтому, если имеем дело со стандартом от c++17, то лучше в функцию принимать явно string_view или const string_view&, если ниже, то принимать надо const string&.

2) если уж принимаем что-то строкоподобное, то смело пользуемся функцией-членом ::find, которая есть и в строке и в вью. С её помощью можно найти сразу позицию арифметического символа. Для простого случая, даже не нужен обход от найденной позиции к началу строки и к концу строки для проверок std::isspace, является ли символ пробелом, поскольку мы можем сразу слать в atoi для левого числа например string_view::substr(0, opPos - 1). И, получится, только один if/switch, который будет сопоставлять символ операции, например, с указателем на функцию.
...
int plus(int left, int right) { return left + right; }

using CalculatorFunction = int (*)(int, int);
...
CalculatorFunction operation;
...
switch (opChar):
case '+': operation = plus;
...


3) настоятельно рекомендую после этого реализовать нормальный простой калькулятор, а потом третьим заходом добавить поддержку скобок, hex oct бинарную и с мантиссой нотации записи чисел, операцию степени и корня.
Нормальный - имеется ввиду, что в калькулятор входит строка, которая является корректным арифметическим выражением. В этом случае нужно ещё иметь дело с разделителями. По-хорошему, решение состоит из нескольких этапов:

- на первом логическом этапе функция парсит выражение и сохраняет в контексте или возвращает контейнер с токенами, (или прерывает весь процесс из-за плохой строки). В вашем варианте, задача однопоточная, поэтому можно воспользоваться функцией strtok, чтобы удобно извлечь строковое значение токена по входной строке от разделителя до разделителя.

- как вариант, токены могут быть структурами с членом string_view на строковые представления и членом на тип токена (число, операция).

- на втором логическом этапе мы считываем последовательность токенов и делаем какое-то действие:
  • считали число - ожидаем за ним токен операции
  • если оказался не токен операции - бросаем исключение о некорректном арифметическом выражении, иначе - подготавливаем аргументы и кормим функцию evaluate<T>( Token::kOperation op, T arg1, T arg2) , для случая, если операции нужно два аргумента, то первый уже считан, а второй будет в последовательности токенов за операцией. Результат evalaute записываем в переменную результата, которая, кстати говоря, типа double.
  • продолжаем движение по последовательности токенов до конца, контейнером может быть std::list, когда добрались до конца, возвращаем результат.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@res2001
Developer, ex-admin
Например:
atoi(ex.c_str())+atoi(ex.c_str())
это выражение 2 раза преобразует в число одну и ту же строку, а затем складывает эти 2 одинаковых числа.
Так что "не дублирует", а выполняет то что написано у вас в коде.

Вам нужно сначала разделить строку на "токены", т.е. на операнды и операции.
Если вы на этом этапе расчитываете обрабатывать только простейшие действия, то вводите строку и разбиваете ее на 3 токена. Затем операнды преобразуете в числа и после этого уже выполняете действие.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы