Опубликован: 04.12.2009 | Доступ: свободный | Студентов: 8424 / 660 | Оценка: 4.30 / 3.87 | Длительность: 27:27:00
Лекция 5:

Управляющие конструкции

< Лекция 4 || Лекция 5: 123 || Лекция 6 >

5.7. Оператор цикла while – цикл с предусловием

while(условие)
    оператор;

Пока условие сохраняет значение true — в цикле выполняется оператор, иначе — действие цикла прекращается. Если условие с самого начала false, цикл сразу прекращается, и тело цикла не выполнится ни разу.

Цикл while обычно применяют вместо цикла for в том случае, если условия продолжения достаточно сложные. В отличие от цикла for в этом случае нет формально заданного счетчика цикла, и не производится его автоматического изменения. За это отвечает программист. Хотя вполне возможно использование как цикла for вместо while, так и наоборот. Многие программисты предпочитают пользоваться только циклом for как наиболее универсальным.

Пример:

i=1;
x=0;
while(i<=n){
  x+=i;//эквивалентно x=x+i;
  i*=2;//эквивалентно i=2*i;
};

В операторе while очень часто совершают ошибки, приводящие к неустойчивости алгоритмов из-за сравнения чисел с плавающей точкой на неравенство. Как мы знаем, сравнивать их на равенство в подавляющем большинстве случаев некорректно из-за ошибок представления таких чисел в компьютере. Но большинство программистов почему-то считает, что при сравнении на неравенство проблем не возникает, хотя это не так. Например, если организовать с помощью оператора while цикл с вещественным счетчиком, аналогичный разобранному в разделе, посвященному циклу for. Пример типичной ошибки в организации такого цикла приведен ниже:

double a=…;
double b=…;
double dx=…;
double x=a;
while(x<=b){
  …
  x=x+dx;
};

Как мы уже знаем, данный цикл будет обладать неустойчивостью в случае, когда на интервале от a до b укладывается целое число шагов. Например, при a=0, b=10, dx=0.1 тело цикла будет выполняться при x=0, x=0.1, …, x=9.9. А вот при x=10 тело цикла может либо выполниться, либо не выполниться – как повезет! Причина связана с конечной точностью выполнения операций с числами в формате с плавающей точкой. Величина шага dx в двоичном представлении чуть-чуть отличается от значения 0.1, и при каждом цикле систематическая погрешность в значении x накапливается. Поэтому точное значение x=10 достигнуто не будет, величина x будет либо чуть-чуть меньше, либо чуть-чуть больше. В первом случае тело цикла выполнится, во втором – нет. То есть пройдет либо 100, либо 101 итерация (число выполнений тела цикла).

5.8. Оператор цикла do...while – цикл с постусловием

do
  оператор;
while(условие);

Если условие принимает значение false, цикл прекращается. Тело цикла выполняется до проверки условия, поэтому оно всегда выполнится хотя бы один раз.

Пример:

int i=0;
double x=1;
do{
  i++; // i=i+1;
  x*=i; // x=x*i;
}
while(i<n);

Если с помощью оператора do…while организуется цикл с вещественным счетчиком или другой проверкой на равенство или неравенство чисел типа float или double, у него возникают точно такие же проблемы, как описанные для циклов for и while.

При необходимости организовать бесконечный цикл (с выходом изнутри тела цикла с помощью оператора прерывания) часто используют следующий вариант:

do{
  …
}
while(true);

5.9. Операторы прерывания continue, break, return, System.exit

Довольно часто требуется при выполнении какого-либо условия прервать цикл или подпрограмму и перейти к выполнению другого алгоритма или очередной итерации цикла. При неструктурном программировании для этих целей служил оператор goto. В Java имеются более гибкие и структурные средства для решения этих проблем - операторы continue, break, return, System.exit:

  • continue; – прерывание выполнения тела цикла и переход к следующей итерации (проверке условия) текущего цикла;
  • continue имя метки; – прерывание выполнения тела цикла и переход к следующей итерации (проверке условия) цикла, помеченного меткой ( label );
  • break; – выход из текущего цикла;
  • break имя метки; – выход из цикла, помеченного меткой;
  • return; – выход из текущей подпрограммы (в том числе из тела цикла) без возврата значения;
  • return значение; – выход из текущей подпрограммы (в том числе из тела цикла) с возвратом значения;
  • System.exit(n) –выход из приложения с кодом завершения n. Целое число n произвольно задается программистом. Если n=0, выход считается нормальным, в других случаях - аварийным. Приложение перед завершением сообщает число n операционной системе для того, чтобы программист мог установить, по какой причине произошел аварийный выход.

Операторы continue и break используются в двух вариантах – без меток для выхода из текущего (самого внутреннего по вложенности) цикла, и с меткой - для выхода из помеченного ей цикла. Меткой является идентификатор, после которого стоит двоеточие. Метку можно ставить непосредственно перед ключевым словом, начинающим задание цикла ( for, while, do ).

Пример использования continue без метки:

for(int i=1;i<=10;i++){
    if(i==(i/2)*2){
        continue;
    };
    System.out.println("i="+i);
};

В данном цикле не будут печататься все значения i, для которых i==(i/2)*2. То есть выводиться в окно консоли будут только нечетные значения i.

Еще один пример использования continue без метки:

for(int i=1;i<=20;i++){
for(int j=1;j<=20;j++){
    if(i*j==(i*j/2)*2){
        continue;
    };
    System.out.println("i="+i+" j="+j+ " 1.0/(i*j-20)="+ (1.0/(i*j-20)) );
};
};

В этом случае будут выводиться значения i, j и 1.0/(i*j-20) для всех нечетных i и j от 1 до 19 . То есть будут пропущены значения для всех четных i и j:

i=1 j=1 1.0/(i*j-20)=-0.05263157894736842
i=1 j=3 1.0/(i*j-20)=-0.058823529411764705
i=1 j=5 1.0/(i*j-20)=-0.06666666666666667
i=1 j=7 1.0/(i*j-20)=-0.07692307692307693
i=1 j=9 1.0/(i*j-20)=-0.09090909090909091
i=1 j=11 1.0/(i*j-20)=-0.1111111111111111
i=1 j=13 1.0/(i*j-20)=-0.14285714285714285
i=1 j=15 1.0/(i*j-20)=-0.2
i=1 j=17 1.0/(i*j-20)=-0.3333333333333333
i=1 j=19 1.0/(i*j-20)=-1.0
i=3 j=1 1.0/(i*j-20)=-0.058823529411764705
i=3 j=3 1.0/(i*j-20)=-0.09090909090909091
i=3 j=5 1.0/(i*j-20)=-0.2
i=3 j=7 1.0/(i*j-20)=1.0
...
i=19 j=9 1.0/(i*j-20)=0.006622516556291391
i=19 j=11 1.0/(i*j-20)=0.005291005291005291
i=19 j=13 1.0/(i*j-20)=0.004405286343612335
i=19 j=15 1.0/(i*j-20)=0.0037735849056603774
i=19 j=17 1.0/(i*j-20)=0.0033003300330033004
i=19 j=19 1.0/(i*j-20)=0.002932551319648094

Пример использования continue с меткой:

label_for1:
  for(int i=1;i<=20;i++){
  for(int j=1;j<=20;j++){
      if(i*j==(i*j/2)*2){
          continue label_for1;
      };
      System.out.println("i="+i+" j="+j+ " 1.0/(i*j-20)="+ (1.0/(i*j-20)) );
};
};

В отличие от предыдущего случая, после каждого достижения равенства i*j==(i*j/2)*2 будет производиться выход из внутреннего цикла (по j), и все последующие j для таких значений i будут пропущены. Поэтому будут выведены только значения

i=1 j=1 1.0/(i*j-20)=-0.05263157894736842
i=3 j=1 1.0/(i*j-20)=-0.058823529411764705
i=5 j=1 1.0/(i*j-20)=-0.06666666666666667
i=7 j=1 1.0/(i*j-20)=-0.07692307692307693
i=9 j=1 1.0/(i*j-20)=-0.09090909090909091
i=11 j=1 1.0/(i*j-20)=-0.1111111111111111
i=13 j=1 1.0/(i*j-20)=-0.14285714285714285
i=15 j=1 1.0/(i*j-20)=-0.2
i=17 j=1 1.0/(i*j-20)=-0.3333333333333333
i=19 j=1 1.0/(i*j-20)=-1.0

Пример использования break без метки:

for(int i=1;i<=10;i++){
    if( i+6== i*i ){
        break;
    };
    System.out.println("i="+i);
};

Данный цикл остановится при выполнении условия i+6== i*i. То есть вывод в окно консоли будет только для значений i, равных 1 и 2.

Еще один пример использования break без метки:

for(int i=1;i<=20;i++){
for(int j=1;j<=20;j++){
    if(i*j==(i*j/2)*2){
        break;
    };
    System.out.println("i="+i+" j="+j+ " 1.0/(i*j-20)="+ (1.0/(i*j-20)) );
};
};

В этом случае будут выводиться все значения i и j до тех пор, пока не найдется пара i и j, для которых i*j==(i*j/2)*2. После чего внутренний цикл прекращается – значения i, j и 1.0/(i*j-20) для данного и последующих значений j при соответствующем i не будут выводиться в окно консоли. Но внешний цикл (по i) будет продолжаться, и вывод продолжится для новых i и j. Результат будет таким же, как для continue с меткой для внешнего цикла.

Пример использования break с меткой:

label_for1:
  for(int i=1;i<=20;i++){
  for(int j=1;j<=20;j++){
      if(i*j==(i*j/2)*2){
          break label_for1;
      };
      System.out.println("i="+i+" j="+j+ " 1.0/(i*j-20)="+ (1.0/(i*j-20)) );
};
};

В этом случае также будут выводиться все значения i и j до тех пор, пока не найдется пара i и j, для которых i*j==(i*j/2)*2. После чего прекращается внешний цикл, а значит – и внутренний тоже. Так что вывод в окно консоли прекратится. Поэтому вывод будет только для i=1, j=1.

Краткие итоги

  • В Java имеются: условный оператор if, оператор выбора switch, условное выражение …?... : … , операторы инкремента ++ и декремента --,
  • В Java имеются операторы цикла: for, whileцикл с предусловием, do...whileцикл с постусловием. А также операторы прерывания циклов continue и break, подпрограмм - return, программы - System.exit.
  • Сравнение на равенство чисел в формате с плавающей точкой практически всегда некорректно. Но даже сравнение на неравенство таких чисел нельзя выполнять в случае, когда в точной арифметике эти числа должны быть равны. По этой причине нельзя использовать циклы с вещественным счетчиком в случаях, когда на интервале изменения счетчика укладывается целое число шагов счетчика.

Задания

  • Написать приложение с графическим пользовательским интерфейсом, в котором по нажатию на кнопку иллюстрируется действие операторов цикла for , while , do...while – в зависимости от того, какая из кнопок JRadioButton нажата. С помощью этих циклов должна вычисляться площадь под кривой, задаваемой функцией f(x)=a\cdot x^2+b\cdot x+c, при x меняющемся от x1 до x2, где величины a,b,c, x1 и x2 должны вводиться пользователем в соответствующих пунктах ввода.
  • Добавить в это приложение вычисление с вещественным счетчиком цикла для какого-либо из операторов ( for , while или do...while ) в случае, когда отмечена соответствующая опция с помощью кнопки JCheckBox. Показать наличие неустойчивости при очень малом изменении входных параметров (какой-либо из величин a,b,c, x1,x2 ). Объяснить, почему изменение одних параметров приводит к неустойчивости, а других – нет.
  • Написать приложение с графическим пользовательским интерфейсом, в котором по нажатию на кнопку JButton иллюстрируется действие операторов прерывания continue, break, return, System.exit – в зависимости от того, какая из кнопок JToggleButton нажата.
< Лекция 4 || Лекция 5: 123 || Лекция 6 >
Полетаев Дмитрий
Полетаев Дмитрий
Не очень понятно про оболочечные Данные,ячейки памяти могут наверно размер менять,какое это значение те же операции только ячейки больше,по скорости тоже самое
Максим Старостин
Максим Старостин

Код с перемещением фигур не стирает старую фигуру, а просто рисует новую в новом месте. Точку, круг.