// 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. Назад к школьной доске!