Подтвердить что ты не робот

Добавляем! оператор и sqrt(), pow() и т.д. для примера приложения калькулятора

Я делаю упражнения в новой книге Страуструпа "Принципы и практика программирования с использованием C++", и мне было интересно, выполнил ли кто-нибудь из них в Кару и готов ли поделиться своими знаниями?

В частности, о калькуляторе, который был разработан в главах 6 и 7. Например, вопросы о добавлении оператора ! и sqrt(), pow() и т.д. Я сделал это, но я не знаю, если решение я Имеется "хороший" способ делать вещи, и на сайте Bjarne нет опубликованных решений. Я хотел бы знать, иду ли я по правильному пути. Может быть, мы можем сделать вики для упражнений?

В основном у меня есть парсер токена. Он читает символ за раз из cin. Это означало токенизировать выражения типа 5 * 3 + 1, и это прекрасно работает для этого. Одним из упражнений является добавление функции sqrt(). Поэтому я изменил код токенизации, чтобы обнаружить "sqrt ("), а затем вернуть объект Token, представляющий sqrt. В этом случае я использую символ "s". Это так, как это сделали бы другие? Что если мне нужно реализовать sin()? Изложение дела может стать грязным.

char ch;
cin >> ch;    // Note that >> skips whitespace (space, newline, tab, etc.)

switch (ch) {
    case ';':    // For "print"
    case 'q':    // For "quit"
    case '(':
    case ')':
    case '+':
    case '-':
    case '*':
    case '/':
    case '!':
        return Token(ch);        // Let each character represent itself
    case '.':
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
        {
            cin.putback(ch);         // Put digit back into the input stream
            double val;
            cin >> val;              // Read a floating-point number
            return Token('8', val);   // Let '8' represent "a number"
        }
    case 's':
        {
            char q, r, t, br;
            cin >> q >> r >> t >> br;
            if (q == 'q' && r == 'r' && t == 't' && br == '(') {
                cin.putback('(');    // Put back the bracket
                return Token('s');   // Let 's' represent sqrt
            }
        }

    default:
        error("Bad token");
}
4b9b3361

Ответ 1

  1. В Stroustrup - Программирование опубликовано несколько решений, и со временем появятся и другие.

  2. Попробуйте решать упражнения только с использованием языковых возможностей и библиотечных средств, представленных в книге - настоящие начинающие пользователи больше ничего не могут сделать. Затем вернитесь позже, чтобы узнать, как можно улучшить решение.

Ответ 2

Я подумал, что карта строк для указателей на функции может быть кратким способом представления таких вещей, как sqrt, sin, cos и т.д., Которые принимают одинарное значение типа double и возвращают значение типа double:

map<std::string, double (*)(double)> funcs;
funcs["sqrt"] = &sqrt;
funcs["sin"] = &sin;
funcs["cos"] = &cos;

Затем, когда анализатор обнаружит правильную строку (str), он может вызвать функцию с аргументом (arg), например, так:

double result = funcs[str](arg);

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

На самом деле я не уверен, что это правильный синтаксис, кто-нибудь может подтвердить?

Это похоже на пригодный для использования метод?

Ответ 3

Работать с производными классами и виртуальными функциями проще: каждый специализированный класс читает свой ввод...

class base {
    public:
        virtual double calc() = 0;
};

class get_sqrt : public base {
    int useless;

    public:
        virtual double calc() {
            cin >> number;
            return sqrt(number);
        }
}

get_sqrt;

Теперь мы организуем их на карте и будем использовать только их указатели:

map<string,base*> func;
func["sqrt"] = &get_sqrt;

Существует также специальный метод, который просматривает только следующий символ: peek();

char c = cin.peek();

Вы можете избавиться от переключателя, используя 1, если поместить !, +, - и т.д. в func; (они должны работать на left_param для простоты):

 if (c>='0' && c<='9')
     cin >> right_param; // Get a number, you don't have to put the
                         // character back as it hasn't been removed
 else {
     string s;
     cin >> s;
     right_param = func[s]->calc();
 }

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

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

Ответ 4

Я бы переместил обнаружение "sqrt" в другой метод обнаружения функции. По сути, я бы удалил обнаружение 's' и добавил что-то внутри случая по умолчанию, которое будет читать строку до '('.

Если no '(' обнаружен, то ошибка.

Если вы успешно прочитали строку, перешлите ее к парсеру имен функций, который использует строку, сравнивает сгенерирует токен, представляющий вызов sqrt или sin или любую другую функцию, которая вам нравится. Метод, который проверяет имена функций, также может быть ошибкой, если он читает строку, которую он не распознает.