Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

      left += term();       // вычисляем Терм и добавляем его

    else

      left –= term();       // вычисляем Терм и вычитаем его

    t = get_token();

  }

  return left;              // финал: символов + и – нет; возвращаем ответ

}

Этот вариант немного сложнее: мы ввели цикл для поиска символов + и . Кроме того, дважды повторили проверку символов + и , а также дважды вызвали функцию get_token(). Поскольку это запутывает логику кода, просто продублируем проверку символов + и .

double expression()

{

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

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

  while(true) {

    switch(t.kind) {

    case '+':

      left += term();    // вычисляем Терм и добавляем его

      t = get_token();

      break;

    case '–':

      left –= term();    // вычисляем Терм и вычитаем его

      t = get_token();

      break;

    default:

      return left;       // финал: символов + и – нет;

                         // возвращаем ответ

    }

  }

}

Обратите внимание на то, что — за исключением цикла — этот вариант напоминает первый (см. раздел 6.5.2.1). Мы просто удалили вызов функции expression() в функции expression() и заменили его циклом. Другими словами, перевели Выражение в грамматическом правиле в цикл поиска Терма, за которым следует символ + или .

<p id="AutBody_Root106"><strong>6.5.3. Термы</strong></p>

Грамматическое правило для Терма очень похоже на правило для Выражения.

Терм:

  Первичное выражение

  Терм '*' Первичное выражение

  Терм '/' Первичное выражение

  Терм '%' Первичное выражение

Следовательно, программный код также должен быть похож на код для Выражения. Вот как выглядит его первый вариант:

double term()

{

  double left = primary();

  Token t = get_token();

  while(true) {

    switch (t.kind) {

    case '*':

      left *= primary();

      t = get_token();

      break;

    case '/':

      left /= primary();

      t = get_token();

      break;

    case '%':

      left %= primary();

      t = get_token();

      break;

    default:

      return left;

    }

  }

}

  К сожалению, этот код не компилируется: операция вычисления остатка (%) для чисел с плавающей точкой не определена. Компилятор вежливо предупредит нас об этом. Когда мы утвердительно ответили на вопрос 5 из раздела 6.3.5 — “Следует ли позволить ввод чисел с плавающей точкой?”, — мы не думали о таких последствиях и просто поддались искушению добавить в программу дополнительные возможности. Вот так всегда! Что же делать? Можно во время выполнения программы проверить, являются ли оба операнда операции % целыми числами, и сообщить об ошибке, если это не так. А можно просто исключить операцию % из возможностей нашего калькулятора. Эту функцию всегда можно добавить позднее (см. раздел 7.5). Исключив операцию %, получим вполне работоспособную функцию: термы правильно распознаются и вычисляются. Однако опытный программист заметит нежелательную деталь, которая делает функцию term() неприемлемой. Что произойдет, если ввести выражение 2/0? На нуль делить нельзя. Если попытаться это сделать, то аппаратное обеспечение компьютера обнаружит это и прекратит выполнение программы, выдав сообщение об ошибке. Неопытный программист обязательно столкнется с этой проблемой. По этой причине лучше провести проверку и выдать подходящее сообщение.

double term()

{

  double left = primary();

  Token t = get_token();

  while(true) {

    switch (t.kind) {

    case '*':

      left *= primary();

      t = get_token();

      break;

    case '/':

    { double d = primary();

      if (d == 0) error("деление на нуль");

      left /= d;

      t = get_token();

    break;

    }

    default:

      return left;

    }

  }

 }

Почему мы поместили обработку операции / внутри блока? На этом настоял компилятор. Если мы хотим определить и инициализировать переменные в операторе switch, то должны поместить ее в блоке.

<p id="AutBody_Root107"><strong>6.5.4. Первичные выражения</strong></p>

Грамматическое правило для первичных выражений также простое.

Первичное выражение:

  Число

  '('Выражение')'

Перейти на страницу:

Похожие книги

Programming with POSIX® Threads
Programming with POSIX® Threads

With this practical book, you will attain a solid understanding of threads and will discover how to put this powerful mode of programming to work in real-world applications. The primary advantage of threaded programming is that it enables your applications to accomplish more than one task at the same time by using the number-crunching power of multiprocessor parallelism and by automatically exploiting I/O concurrency in your code, even on a single processor machine. The result: applications that are faster, more responsive to users, and often easier to maintain. Threaded programming is particularly well suited to network programming where it helps alleviate the bottleneck of slow network I/O. This book offers an in-depth description of the IEEE operating system interface standard, POSIX (Portable Operating System Interface) threads, commonly called Pthreads. Written for experienced C programmers, but assuming no previous knowledge of threads, the book explains basic concepts such as asynchronous programming, the lifecycle of a thread, and synchronization. You then move to more advanced topics such as attributes objects, thread-specific data, and realtime scheduling. An entire chapter is devoted to "real code," with a look at barriers, read/write locks, the work queue manager, and how to utilize existing libraries. In addition, the book tackles one of the thorniest problems faced by thread programmers-debugging-with valuable suggestions on how to avoid code errors and performance problems from the outset. Numerous annotated examples are used to illustrate real-world concepts. A Pthreads mini-reference and a look at future standardization are also included.

David Butenhof

Программирование, программы, базы данных