Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

case '+':

return left + term; // считываем и вычисляем Терм,

// затем выполняем сложение

case '–':

return left – term; // считываем и вычисляем Терм,

// затем выполняем вычитание

default:

return left; // возвращаем значение Выражения

}

}

Программа выглядит неплохо. Это почти тривиальная транскрипция грамматики. Она довольно проста: сначала считываем Выражение, а затем проверяем, следует ли за ним символ + или –, и в случае положительного ответа считываем Терм.

К сожалению, на самом деле этот программный код содержит мало смысла. Как узнать, где кончается выражение, чтобы искать символ + или –? Напомним, что наша программа считывает символы слева направо и не может заглянуть вперед, чтобы узнать, нет ли там символа +. Фактически данный вариант функции

expression
никогда не продвинется дальше своей первой строки: функция
expression
начинает работу с вызова функции
expression
, которая, в свою очередь, начинается с вызова функции
expression
, и так до бесконечности. Этот процесс называется бесконечной рекурсией, но на самом деле он довольно быстро заканчивается, исчерпав память компьютера. Термин рекурсия используется для описания процесса, который выполняется, когда функция вызывает саму себя. Не любая рекурсия является бесконечной; рекурсия является очень полезным методом программирования (см. раздел 8.5.8).

6.5.2.2. Выражения: вторая попытка

Итак, что же мы делаем? Каждый Терм является Выражением, но не любое Выражение является Термом; иначе говоря, можно начать поиск с Терма и переходить к поиску полного Выражения, только обнаружив символ + или –. Рассмотрим пример.

double expression

{

double left = Term; // считываем и вычисляем Терм

Token t = get_token; // получаем следующую лексему

switch (t.kind) { // определяем вид лексемы

case '+':

return left + expression; // считываем и вычисляем

// Выражение, затем

// выполняем сложение

case '–':

return left – expression; // считываем и вычисляем

// Выражение, затем

// выполняем вычитание

default:

return left; // возвращаем значение Терма

}

}

Этот программный код действительно — более или менее — работает. Мы включим его в окончательный вариант программы для грамматического разбора правильных выражений и отбраковки неправильных. Он позволяет правильно вычислить большинство выражений. Например, выражение 1+2 считывается как Терм (имеющий значение 1), за которым следует символ +, а за ним — Выражение (которое оказывается Термом, имеющим значение

2
). В итоге получаем ответ, равный 3. Аналогично, выражение 1+2+3 дает ответ 6. Можно было бы много говорить о том, что эта программа делает хорошо, но мы сразу поставим вопрос ребром: а чему равно выражение 1–2–3? Функция
expression
считает число 1 как Терм, затем переходит к считыванию 2–3 как Выражения (состоящего их Терма 2, за которым следует Выражение 3). Таким образом, из 1 будет вычтено значение выражения 2–3. Иначе говоря, программа вычисляет выражение 1–(2–3). Оно равно 2. Однако мы еще со школьной скамьи знаем, что выражение 1–2–3 означает (1–2)–3 и, следовательно, равно –4.

Итак, мы написали превосходную программу, которая выполняет вычисления неправильно. Это опасно. Это особенно опасно, поскольку во многих случаях программа дает правильный ответ. Например, выражение 1+2+3 будет вычислено правильно (6), так как 1+(2+3) эквивалентно (1+2)+3.

Что же мы сделали неправильно с точки зрения программирования? Этот вопрос следует задавать себе каждый раз, когда обнаружите ошибку. Именно так мы можем избежать повторения одних и тех же ошибок. По существу, мы просто просмотрели программный код и угадали правильное решение. Это редко срабатывает! Мы должны понять, как работает программа, и объяснить, почему она работает правильно.

Анализ ошибок — часто лучший способ найти правильное решение. В данном случае функция

expression
сначала искала Терм, а затем, если за Термом следовал символ + или –, искала Выражение. На самом деле функция реализовала немного другую грамматику.

Выражение:

Терм

Терм '+' Выражение // сложение

Терм '–' Выражение // вычитание

Отличие от нашей грамматики заключается именно в том, что выражение 1–2–3 должно трактоваться как Выражение 1–2, за которым следует символ – и Терм 3, а на самом деле функция интерпретирует выражение 1–2–3 как Терм 1, за которым следует символ – и Выражение 2–3. Иначе говоря, мы хотели, чтобы выражение 1–2–3 было эквивалентно (1–2)–3 , а не 1–(2–3).

  • Читать дальше
  • 1
  • ...
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: