// char и double
};
Эти две функции-члена называют Token. Рассмотрим пример.
Token t1('+'); // инициализируем t1, так что t1.kind = '+'
Token t2('8',11.5); // инициализируем t2,
// так что t2.kind = '8' и t2.value = 11.5
В первом конструкторе фрагмент :kind(ch), value(0) означает “инициализировать член kind значением переменной ch и установить член value равным нулю”. Во втором конструкторе фрагмент :kind(ch), value(val) означает “инициализировать член kind значением переменной ch и установить член value равным переменной val”. В обоих вариантах нам требуется лишь создать объект класса Token, поэтому тело функции ничего не содержит: { }. Специальный синтаксис инициализации (список инициализации членов класса) начинается с двоеточия и используется только в конструкторах.
Обратите внимание на то, что конструктор не возвращает никаких значений, потому что в конструкторе это не предусмотрено. (Подробности изложены в разделах 9.4.2 и 9.7.)
6.3.4. Использование лексем
Итак, похоже, что мы можем завершить нашу программу, имитирующую калькулятор! Однако следует уделить немного времени для планирования. Как использовать класс Token в калькуляторе?
Можно считать входную информацию в вектор объектов Token.
Token get_token(); // считывает объекты класса Token из потока cin
vector
int main()
{
while (cin) {
Token t = get_token();
tok.push_back(t);
}
// ...
}
Теперь можно сначала считать выражение, а вычислить его позднее. Например, для выражения 11*12 получим следующие лексемы:
Эти лексемы можно использовать для поиска операции умножения и ее операндов. Это облегчает выполнение умножения, поскольку числа 11 и 12 хранятся как числовые значения, а не как строки.
Рассмотрим теперь более сложные выражения. Выражение 1+2*3 состоит из пяти объектов класса Token.
Теперь операцию умножения можно выполнить с помощью простого цикла.
for (int i = 0; i
if (tok[i].kind=='*') { // мы нашли умножение!
double d = tok[i–1].value*tok[i+1].value;
// и что теперь?
}
}
Да, и что теперь? Что делать с произведением d? Как определить порядок выполнения частичных выражений? Хорошо, символ + предшествует символу *, поэтому мы не можем выполнить операции просто слева направо. Можно попытаться выполнить их справа налево! Этот подход сработает для выражения 1+2*3, но не для выражения 1*2+3. Рассмотрим выражение 1+2*3+4. Это пример “внутренних вычислений”: 1+(2*3)+4. А как обработать скобки? Похоже, мы зашли в тупик. Теперь необходимо вернуться назад, прекратить на время программировать и подумать о том, как считывается и интерпретируется входная строка и как вычисляется арифметическое выражение.
ПОПРОБУЙТЕ
С другой стороны, почему невозможно найти простое решение этой задачи? Ведь она не выглядит слишком сложной. Такая попытка позволит глубже понять задачу и ее решение. Сразу же определите, что следует сделать. Например, проанализируйте строку 12.5+2. Ее можно разбить на лексемы, понять, что выражение простое, и вычислить ответ. Это может оказаться несколько запутанным, но прямым решением, поэтому, возможно, следовало бы идти в этом направлении! Определите, что следует сделать, если строка содержит операции + и * в выражении 2+3*4? Его также можно вычислить с помощью “грубой силы”. А что делать с более сложным выражением, например 1+2*3/4%5+(6–7*(8))? И как выявлять ошибки, такие как 2+*3 и 2&3? Подумайте об этом, опишите на бумаге возможные решения, используя интересные или типичные арифметические выражения.
6.3.5. Назад к школьной доске!