Программирование. Принципы и практика использования 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;
Поделиться:
Популярные книги
Наваждение генерала драконов
3. Генералы драконов
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Средневековая история. Тетралогия
Средневековая история
Фантастика:
фэнтези
попаданцы
9.16
рейтинг книги
Прогрессор поневоле
2. Фараон
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Тайный наследник для миллиардера
Любовные романы:
современные любовные романы
5.20
рейтинг книги
Лорд Системы 4
4. Лорд Системы
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Вперед в прошлое 5
5. Вперед в прошлое
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Энфис 4
4. Эрра
Фантастика:
городское фэнтези
рпг
аниме
5.00
рейтинг книги
Князь
3. Варяг
Фантастика:
альтернативная история
9.15
рейтинг книги
В теле пацана
1. Великое плато Вита
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Авиатор: назад в СССР
1. Авиатор
Фантастика:
попаданцы
альтернативная история
5.25
рейтинг книги
Сонный лекарь 7
7. Сонный лекарь
Фантастика:
альтернативная история
аниме
5.00
рейтинг книги
Я снова граф. Книга XI
11. Дорогой барон!
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Огни Эйнара. Долгожданная
1. Эйнар
Любовные романы:
любовно-фантастические романы
эро литература
5.00
рейтинг книги
Я – Орк. Том 3
3. Я — Орк
Фантастика:
юмористическое фэнтези
попаданцы
5.00