Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:
Модифицируем функцию
expression
так, чтобы она не “съедала” лексемы. Куда поместить следующую лексему (t
), если программа никак не использует ее? Можно рассмотреть много сложных схем, но давайте просто перейдем к очевидному ответу (его очевидность станет ясной позднее): поскольку лексема будет использована другой функцией, которая будет считывать ее из потока ввода, давайте вернем лексему обратно в поток ввода, чтобы ее могла считать другая функция! Действительно, мы можем вернуть символ обратно в поток ввода, но это не совсем то, что мы хотим. Мы хотим работать с лексемами, а не возиться с символами. Итак, хотелось бы, чтобы поток ввода работал с лексемам, а мы имели бы возможность
Предположим, в нашем распоряжении есть поток лексем — “
Token_stream
” — с именем ts
. Допустим также, что поток Token_stream
имеет функцию-член get
, возвращающую следующую лексему, и функцию-член putback(t)
, возвращающую лексему t
обратно в поток. Мы реализуем класс
Token_stream
в разделе 6.8, как только увидим, как его следует использовать. Имея поток Token_stream
, можем переписать функцию expression
так, чтобы она записывала неиспользованную лексему обратно в поток Token_stream
.
double expression
{
double left = term; // считываем и вычисляем Терм
Token t = ts.get; // получаем следующую лексему
// из потока лексем
while(true) {
switch(t.kind) {
case '+':
left += term; // вычисляем и добавляем Терм
t = ts.get;
break;
case '–':
left –= term; // вычисляем и вычитаем Терм
t = ts.get;
break;
default:
ts.putback(t); // помещаем объект t обратно
// в поток лексем
return left; // финал: символов + и – нет;
// возвращаем ответ
}
}
}
Кроме того, такие же изменения следует внести в функцию
term
.
double term
{
double left = primary;
Token t = ts.get; // получаем следующую лексему
// из потока лексем
while(true) {
switch (t.kind) {
case '*':
left *= primary;
t = ts.get;
break;
case '/':
{
double d = primary;
if (d == 0) error("деление на нуль");
left /= d;
t = ts.get;
break;
}
default:
ts.putback(t); // помещаем объект t обратно в поток лексем
return left;
}
}
}
Для
primary
достаточно заменить функцию get_token
функцией ts.get
; функция primary
использует каждую лексему, которую она считывает. 6.7. Испытание второй версии
Итак, мы готовы к испытанию второй версии. Введем число
2
и символ перехода на новую строку. Нет ответа. Попробуйте ввести еще один символ перехода на новую строку, чтобы убедиться, что компьютер не завис. По-прежнему нет ответа. Введите число 3
и символ перехода на новую строку. Ответ равен 2
. Попробуйте ввести выражение 2+2
и символ перехода на новую строку. Ответ равен 3. Экран выглядит следующим образом:
2
3
=2
2+2
=3
Хм... Может быть, наша функция
putback
и ее использование в функции expression
и term
не решает проблему. Попробуем другой тест.
2 3 4 2+3 2*3
= 2
= 3
= 4
= 5
Да! Это правильные ответы! Но последний ответ (
6
) пропущен. Проблема следующей лексемы не решена. Однако на этот раз она заключается не в том, что наш программный код “съедает” символы, а в том, что он вообще не получает информации, пока не будет введено следующее выражение. Результат вычисления выражения не выводится на экран немедленно; он откладывается до тех пор, пока программа не увидит первую лексему следующего выражения. К сожалению, программа не видит эту лексему, пока мы не нажмем клавишу <Enter> после следующего выражения. Эта программа на самом деле не настолько плоха, она просто немного медленно реагирует. Как исправить этот недостаток? Очевидное решение — потребовать немедленно выполнить вывод. Договоримся считать, что каждое выражение завершается точкой с запятой, которая одновременно служит триггером вывода. Кроме того, добавим в программу команду выхода. Для этого подходит символ
q
(первая буква слова quit
(выход)). Функция main
содержит инструкцию
while (cin) cout << "=" << expression << '\n'; // version 1
Заменим ее более запутанной, но более полезной инструкцией.
double val = 0;
while (cin) {
Token t = ts.get;
if (t.kind == 'q') break; // 'q' для выхода
if (t.kind == ';') // ';' для команды "печатать немедленно"
cout << "=" << val << '\n';
else
ts.putback(t);
val = expression;
Поделиться:
Популярные книги
Последний Паладин. Том 2
2. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Зубных дел мастер
1. Зубных дел мастер
Фантастика:
научная фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Истребитель. Ас из будущего
Фантастика:
боевая фантастика
попаданцы
альтернативная история
5.25
рейтинг книги
Честное пионерское! Часть 3
3. Честное пионерское!
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Обгоняя время
13. Девяностые
Фантастика:
попаданцы
5.00
рейтинг книги
Страж. Тетралогия
Страж
Фантастика:
фэнтези
9.11
рейтинг книги
Магия чистых душ
Любовные романы:
любовно-фантастические романы
5.40
рейтинг книги
Имя нам Легион. Том 4
4. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Девятый
1. Девятый
Фантастика:
боевая фантастика
попаданцы
9.15
рейтинг книги
Морской волк. 1-я Трилогия
1. Морской волк
Фантастика:
альтернативная история
8.71
рейтинг книги
Отмороженный 8.0
8. Отмороженный
Фантастика:
постапокалипсис
рпг
аниме
5.00
рейтинг книги
Совершенный: охота
3. Совершенный
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Калибр Личности 1
1. Калибр Личности
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Личник
3. Ермак
Фантастика:
альтернативная история
6.33