<< Пред.           стр. 19 (из 121)           След. >>

Список литературы по разделу

  int iva1=512 jva1=1024, kva1=4096;
  int bufsize;
  // ...
  switch( swt ) {
  case ival:
  bufsize = ival * sizeof( int );
  break;
  case jval:
  bufsize = jval * sizeof( int );
  break;
  case kval:
  bufsize = kval * sizeof( int );
  break;
  }
 (e)
  enum { illustrator = 1, photoshop, photostyler = 2 };
  switch ( ival ) {
  case illustrator:
  --i11us_1icense;
  break;
  case photoshop:
  --pshop_1icense;
  break;
  case photostyler:
  --psty1er_license;
  break;
 }
 5.5. Инструкция цикла for
 Как мы видели, выполнение программы часто состоит в повторении последовательности инструкций – до тех пор, пока некоторое условие остается истинным. Например, мы читаем и обрабатываем записи файла, пока не дойдем до его конца, перебираем элементы массива, пока индекс не станет равным размерности массива минус 1, и т.д. В С++ предусмотрено три инструкции для организации циклов, в частности for и while, которые начинаются проверкой условия. Такая проверка означает, что цикл может закончиться без выполнения связанной с ним простой или составной инструкции. Третий тип цикла, do while, гарантирует, что тело будет выполнено как минимум один раз: условие цикла проверяется по его завершении. (В этом разделе мы детально рассмотрим цикл for; в разделе 5.6 разберем while, а в разделе 5.7 – do while.)
 Цикл for обычно используется для обработки структур данных, имеющих фиксированную длину, таких, как массив или вектор:
 #include
 int main() {
  int ia[ 10 ];
 
  for ( int ix = 0; ix < 10; ++-ix )
  ia[ ix ] = ix;
  vector ivec( ia, ia+10 );
  vector::iterator iter = ivec.begin() ;
 
  for ( ; iter != ivec.end(); ++iter )
  *iter *= 2;
 return 0;
 }
 Синтаксис цикла for следующий:
 for (инструкция-инициализации; условие; выражение )
  инструкция
 инструкция-инициализации может быть либо выражением, либо инструкцией объявления. Обычно она используется для инициализации переменной значением, которое увеличивается в ходе выполнения цикла. Если такая инициализация не нужна или выполняется где-то в другом месте, эту инструкцию можно заменить пустой (см. второй из приведенных ниже примеров). Вот примеры правильного использования инструкции-инициализации:
 // index и iter определены в другом месте
 for ( index =0; ...
 for ( ; /* пустая инструкция */ ...
 for ( iter = ivec.begin(); ...
 for ( int 1o = 0,hi = max; ...
 for ( char *ptr = getStr(); ...
 условие служит для управления циклом. Пока условие при вычислении дает true, инструкция продолжает выполняться. Выполняемая в цикле инструкция может быть как простой, так и составной. Если же самое первое вычисление условия дает false, инструкция не выполняется ни разу. Правильные условия можно записать так:
 (... index < arraySize; ... )
 (... iter != ivec.end(); ... )
 (... *stl++ = *st2++; ... )
 (... char ch = getNextChar(); ... )
 Выражение вычисляется после выполнения инструкции на каждой итерации цикла. Обычно его используют для модификации переменной, инициализированной в инструкции-инициализации. Если самое первое вычисление условия дает false, выражение не выполняется ни разу. Правильные выражения выглядят таким образом:
 ( ... ...; ++-index )
 ( ... ...; ptr = ptr->next )
 ( ... ...; ++i, --j, ++cnt )
 ( ... ...; ) // пустое выражение
 Для приведенного ниже цикла for
 const int sz = 24;
 int ia[ sz ];
 vector ivec( sz );
 
 for ( int ix = 0; ix < sz; ++ix ) {
  ivec[ ix ] = ix;
  ia[ ix ]= ix;
 }
 порядок вычислений будет следующим:
 инструкция-инициализации выполняется один раз перед началом цикла. В данном примере объявляется переменная ix, которая инициализируется значением 0.
 Вычисляется условие. Если оно равно true, выполняется составная инструкция тела цикла. В нашем примере, пока ix меньше sz, значение ix присваивается элементам ivec[ix] и ia[ix]. Когда значением условия станет false, выполнение цикла прекратится. Если самое первое вычисление условия даст false, составная инструкция выполняться не будет.
 Вычисляется выражение. Как правило, его используют для модификации переменной, фигурирующей в инструкции-инициализации и проверяемой в условии. В нашем примере ix увеличивается на 1.
 Эти три шага представляют собой полную итерацию цикла for. Теперь шаги 2 и 3 будут повторяться до тех пор, пока условие не станет равным false, т.е. ix окажется равным или большим sz.
 В инструкции-инициализации можно определить несколько объектов, однако все они должны быть одного типа, так как инструкция объявления допускается только одна:
 for ( int ival = 0, *pi = &ia, &ri = val;
  ival < size;
  ++iva1, ++pi, ++ri )
  // ...
 Объявление объекта в условии гораздо труднее правильно использовать: такое объявление должно хотя бы раз дать значение false, иначе выполнение цикла никогда не прекратится. Вот пример, хотя и несколько надуманный:
 #include
 
 int main()
 {
  for ( int ix = 0;
  bool done = ix == 10;
  ++ix )
  cout << "ix: " << ix << endl;
 }
 Видимость всех объектов, определенных внутри круглых скобок инструкции for, ограничена телом цикла. Например, проверка iter после цикла вызовет ошибку компиляции:
 int main()
 {
  string word;
  vector< string > text;
  // ...
  for ( vector< string >::iterator
  iter = text.begin(),
  iter_end = text.end();
  iter != text.end(); ++iter )
  {
  if ( *iter == word )
  break;
  // ...
  }
 
  // ошибка: iter и iter_end невидимы
  if ( iter != iter_end )
  // ...
 Упражнение 5.8
 Допущены ли ошибки в нижеследующих циклах for? Если да, то какие?
 (a)
  for ( int *ptr = &ia, ix = 0;
  ix < size && ptr != ia+size;
  ++ix, ++ptr )
  // ...
 
 (b)
  for ( ; ; ) {
  if ( some_condition )
  break;
  // ...
  }
  (c)
  for ( int ix = 0; ix < sz; ++ix )
  // ...
 
  if ( ix != sz )
  // ...
 (d)
  int ix;
  for ( ix < sz; ++ix )
  // ...
 (e)
  for ( int ix = 0; ix < sz; ++ix, ++ sz )
  // ...
 Упражнение 5.9
 Представьте, что вам поручено придумать общий стиль использования цикла for в вашем проекте. Объясните и проиллюстрируйте примерами правила использования каждой из трех частей цикла.
 Упражнение 5.10
 Дано объявление функции:
 bool is_equa1( const vector &vl,
  const vector &v2 );
 Напишите тело функции, определяющей равенство двух векторов. Для векторов разной длины сравнивайте только то количество элементов, которое соответствует меньшему из двух. Например, векторы (0,1,1,2) и (0,1,1,2,3,5,8) считаются равными. Длину векторов можно узнать с помощью функций v1.size() и v2.size().
 5.6. Инструкция while
 Синтаксис инструкции while следующий:
 while ( условие )
  инструкция
 Пока значением условия является true, инструкция выполняется в такой последовательности:
 Вычислить условие.
 Выполнить инструкцию, если условие истинно.
 Если самое первое вычисление условия дает false, инструкция не выполняется.
 Условием может быть любое выражение:
 bool quit = false;
 // ...
 while ( ! quit ) {
  // ...
  quit = do_something();
 }
 
 string word;
 while ( cin >> word ){ ... }
 или объявление с инициализацией:
 while ( symbol *ptr = search( name )) {
  // что-то сделать
 }
 В последнем случае ptr видим только в блоке, соответствующем инструкции while, как это было и для инструкций for и switch.
 Вот пример цикла while, обходящего множество элементов, адресуемых двумя указателями:
 int sumit( int *parray_begin, int *parray_end )
 {
  int sum = 0;
 
  if ( ! parray_begin || ! parray_end )
  return sum;
 
  while ( parray_begin != parray_end )
  // прибавить к sum
  // и увеличить указатель
  sum += *parray_begin++;
 
  return sum;
 }
 
 int ia[6] = { 0, 1, 2, 3, 4, 5 };
 int main()
 {
  int sum = sumit( &ia[0], &ia[ 6 ] );
  // ...
 }
 Для того чтобы функция sumit() выполнялась правильно, оба указателя должны адресовать элементы одного и того же массива (parray_end может указывать на элемент, следующий за последним). В противном случае sumit() будет возвращать бессмысленную величину. Увы, С++ не гарантирует, что два указателя адресуют один и тот же массив. Как мы увидим в главе 12, стандартные универсальные алгоритмы реализованы подобным же образом, они принимают параметрами указатели на первый и последний элементы массива.
 Упражнение 5.11
 Какие ошибки допущены в следующих циклах while:
 (a)
  string bufString, word;
  while ( cin >> bufString >> word )
  // ...
 (b)
  while ( vector::iterator iter != ivec.end() )
  // ...
 (c)
  while ( ptr = 0 )
  ptr = find_a_value();
 (d)
  while ( bool status = find( word )) {
  word = get_next_word();
  if ( word.empty() )
  break;
  // ...
  }
  if ( ! status )
  cout << "Слов не найдено\n";
 Упражнение 5.12
 while обычно применяется для циклов, выполняющихся, пока некоторое условие истинно, например, читать следующее значение, пока не будет достигнут конец файла. for обычно рассматривается как пошаговый цикл: индекс пробегает по определенному диапазону значений. Напишите по одному типичному примеру for и while, а затем измените их, используя цикл другого типа. Если бы вам нужно было выбрать для постоянной работы только один из этих типов, какой бы вы выбрали? Почему?
 Упражнение 5.13
 Напишите функцию, читающую последовательность строк из стандартного ввода до тех пор, пока одно и то же слово не встретится два раза подряд либо все слова не будут обработаны. Для чтения слов используйте while; при обнаружении повтора слова завершите цикл с помощью инструкции break. Если повторяющееся слово найдено, напечатайте его. В противном случае напечатайте сообщение о том, что слова не повторялись.
 5.8. Инструкция do while
 Представим, что нам надо написать программу, переводящую мили в километры. Структура программы выглядит так:
 int val;
 bool more = true; // фиктивное значение, нужное для
  // начала цикла
 
 while ( more ) {
  val = getValue();
  val = convertValue(val);
  printValue(val);
  more = doMore();
 }
 Проблема заключается в том, что условие вычисляется в теле цикла. for и while требуют, чтобы значение условия равнялось true до первого вхождения в цикл, иначе тело не выполнится ни разу. Это означает, что мы должны обеспечить такое условие до начала работы цикла. Альтернативой может служить использование do while, гарантирующего выполнение тела цикла хотя бы один раз. Синтаксис цикла do while таков:
 do
  инструкция
 while ( условие );
 инструкция выполняется до первой проверки условия. Если вычисление условия дает false, цикл останавливается. Вот как выглядит предыдущий пример с использованием цикла do while:
 do {
  val = getValue();
  val = convertValue(val);
  printValue(val);
 } while doMore();
 В отличие от остальных инструкций циклов, do while не разрешает объявлять объекты в своей части условия. Мы не можем написать:
 // ошибка: объявление переменной
 // в условии не разрешается
 do {
  // ...
  mumble( foo );
 } while ( int foo = get_foo() ) // ошибка
 потому что до условной части инструкции do while мы дойдем только после первого выполнения тела цикла.
 Упражнение 5.14
 Какие ошибки допущены в следующих циклах do while:
  (a)
 do
  string rsp;
  int vail, va12;
  cout << "Введите два числа: ";
  c-in >> vail >> va12;
  cout << "Сумма " << vail
  << " и " << va12
  << " = " << vail + va12 << "\n\n"
  << "Продолжить? [да][нет] ";
  cin >> rsp;
 while ( rsp[0] != 'n' );
 (b)
 do {
  // ...
 } while ( int iva1 = get_response() );
 (c)
  do {
  int ival = get_response();
  if ( iva1 == some_value() )
  break;
  } while ( iva1 );
 
  if ( !iva1 )
  // ...
 Упражнение 5.15
 Напишите небольшую программу, которая запрашивает у пользователя две строки и печатает результат лексикографического сравнения этих строк (строка считается меньшей, если идет раньше при сортировке по алфавиту). Пусть она повторяет эти действия, пока пользователь не даст команду закончить. Используйте тип string, сравнение строк и цикл do while.
 5.8. Инструкция break
 Инструкция break останавливает циклы for, while, do while и блока switch. Выполнение программы продолжается с инструкции, следующей за закрывающей фигурной скобкой цикла или блока. Например, данная функция ищет в массиве целых чисел определенное значение. Если это значение найдено, функция сообщает его индекс, в противном случае она возвращает -1. Вот как выглядит реализация функции:
 // возвращается индекс элемента или -1
 int search( int *ia, int size, int value )
 {
  // проверка что ia != 0 и size > 0 ...
 
  int loc = -1;
  for ( int ix = 0; ix < size; ++ix ) {
  if ( value == ia[ ix ] ) {
  // нашли!
  // запомним индекс и выйдем из цикла
  1oc = ix;
  break;
  }
  } // конец цикла
 
  // сюда попадаем по break ...
  return 1oc;
 }
 В этом примере break прекращает выполнение цикла for и передает управление инструкции, следующей за этим циклом, – в нашем случае return. Заметим, что break выводит из блока, относящегося к инструкции for, а не if, хотя является частью составной инструкции, соответствующей if. Использование break внутри блока if, не входящего в цикл или в switch, является синтаксической ошибкой:
 // ошибка: неверное использование break
 if ( ptr ) {
  if ( *ptr == "quit" )
  break;

<< Пред.           стр. 19 (из 121)           След. >>

Список литературы по разделу