Выход из цикла с помощью If
Бесконечный цикл. Начнем с привычного. Задача: При помощи цикла печатать такие текст и числа:
Начало счета 3 5 7 9 11 13 15 . . .
Для краткости я во многих программах не буду показывать строки объявлений, заголовков и завершения процедур, а также строку, импортирующую класс Debug. Так что останется одна «суть». Не обращайте также пока внимания на непонятные слова (Do…Loop) в заголовках таблиц, эти слова понадобятся чуть позже. Вот вариант программы. Я назвал его нулевым:
0 ВАРИАНТ(Do …. Loop) |
Write("Начало счета ") f = 3 m: Write(f & " ") f = f + 2 GoTo m |
Выход из цикла. Теперь вопрос: как нам запрограммировать завершение работы цикла, чтобы он не выполнялся бесконечно? Для этого нужно применить оператор GoTo внутри оператора If.
Задача: При помощи цикла напечатать такие текст и числа:
Начало счета 3 5 7 9 11 13 15 . . . . 329 331 333 Конец счета
Для удобства отладки изменим условие задачи – напечатать:
Начало счета 3 5 7 9 Конец счета
Вы уже достаточно опытны в программировании, чтобы догадаться, что программа для измененного условия будет копией программы для исходного, за исключением одного числа.
Вот 4 варианта программы. Первый – самый простой, а остальные нам понадобятся в дальнейшем. Все 4 варианта делают одно и то же, они очень похожи, но чем-то и отличаются. Вот в этом отличии вам и надо разобраться, иначе не будет понятен дальнейший материал.
Создайте проект с 4 кнопками и выполните в пошаговом режиме все 4 варианта:
1 ВАРИАНТ(Do …. Loop While) | 2 ВАРИАНТ (Do …. Loop Until) |
Write("Начало счета ") f = 3 m: Write(f & " ") f = f + 2 If f <= 9 Then GoTo m Write("Конец счета") | Write("Начало счета ") f = 3 m1: Write(f & " ") f = f + 2 If f > 9 Then GoTo m2 Else GoTo m1 m2: Write("Конец счета") |
Вот в каком порядке выполняются операторы программы 1 варианта:
Write("Начало счета") f=3 Write(f & " ") {печатается 3} f=f+2 {f становится равным 5} If f<=9 Then GoTo m Write(f& " ") {печ. 5}f=f+2 {f = 7} If f<=9 Then GoTo m Write(f & " "){печ. 7}f=f+2 {f = 9} If f<=9 Then GoTo m Write(f& " ") {печ. 9}f=f+2 {f = 11} If f<=9 Then GoTo m Write("Конец счета")
Здесь оператор GoTo m выполняется три раза. На четвертый раз условие f<=9 оказывается ложным и поэтому выполняется не GoTo m, а следующий за If оператор Write("Конец счета"), то есть программа выходит из цикла и завершает свою работу.
3 ВАРИАНТ(Do While …. Loop) | 4 ВАРИАНТ(Do Until …. Loop) |
Write("Начало счета ") f = 3 m1: If f <= 9 Then GoTo m3 Else GoTo m2 m3: Write(f & " ") f = f + 2 GoTo m1 m2: Write("Конец счета") | Write("Начало счета ") f = 3 m1: If f > 9 Then GoTo m2 Else GoTo m3 m3: Write(f & " ") f = f + 2 GoTo m1 m2: Write("Конец счета") |
Задача: Напечатать пары чисел - 0 1000 1 999 2 998 . . . . . . 1000 0.
Программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim f, s As Integer
f = 0
m: s = 1000 - f
Write(f & " " & s & " ")
f = f + 1
If f <= 1000 Then GoTo m
End Sub
Задание 25.
А. Напечатать 1 2 3 4 . . . 99 100
Б. Напечатать 100 99 . . . 3 2 1
В. Напечатать 1 2 3 4 . . . 99 100 99 . . . 3 2 1
Задача «Таблицы Брадиса» или «Таблицы логарифмов». В те далекие времена, когда калькуляторов еще не было, были такие напечатанные в виде брошюрок таблицы для школьников и студентов, по которым они могли быстро посмотреть численные значения квадратов, логарифмов и других математических функций. Поставим задачу вычислить и напечатать таблицы Брадиса. На первый раз достаточно вычислить и напечатать с 6 десятичными знаками квадраты и логарифмы чисел 0.001 0.002 0.003 . . . 0.999 1.000.
Программа:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a, Квадрат, Логарифм As Decimal
a = 0.001
m: Квадрат = a ^ 2
Логарифм = Math.Log(a)
Debug.WriteLine(Format(a, "0.000") & " " & Format(Квадрат, "0.000000") _
& " " & Format(Логарифм, "0.000000"))
a = a + 0.001
If a <= 1 Then GoTo m
End Sub
Почему я объявил a как Decimal, а не Double? Причина в незначительных погрешностях, которые допускает компьютер при действиях с десятичными дробями (о чем я уже писал). На моем компьютере при многократном прибавлении 0.001 значение a типа Double на некотором этапе переставало быть точным. Конкретнее, у меня получалось вот что:
0,682 + 0,001 = 0,683000000000001
Вследствие этого, при дальнейшем нарастании а последнее сложение было таким:
0,999000000000001 + 0,001 = 1,000000000000001
Легко видеть, что в этом случае для a=1 задание не выполняется, так как неправда, что
1,000000000000001 <= 1
Тип же Decimal, как я уже писал, обеспечивает абсолютную точность сложений и вычитаний.
Можно было обойтись и типом Double, но в этом случае оператор If пришлось бы чуть-чуть изменить, например, так:
If a < 1.00001 Then GoTo m
Задание 26.
Для х=2700, 900, 300, 100 . . . и т.д. вычислять и печатать y=x/4 + 20 и z=2y+0.23 до тех пор, пока yz не станет меньше 1/х.
Задание 27.
Пусть движущееся изображение, описанное в 8.1.4, через некоторое время остановится.
Задание 28.
Изображение, пройдя немного слева направо, поворачивает вниз и, пройдя немного, через некоторое время останавливается.
Операторы цикла Do
Циклы настолько широко применяются в программах, что у программистов давным-давно появилась потребность написать специальный оператор цикла, не использующий оператор GoTo. Последний неудобен хотя бы тем, что у программистов, пишущих большие программы, много времени и внимания уходит на поиск взглядом меток в тексте программы. К тому же GoTo нарушает стройную идеологию так называемого «структурного программирования», когда порядок действий задается не скачками из одной части программы в другую, а цепочкой вложенных друг в друга операторов. В общем, нынче широко использовать GoTo так же неприлично, как не объявлять переменные.
Операторы цикла в VB делятся на 2 вида: Doи For. Операторы вида Do встречаются в 5 вариантах:
0 ВАРИАНТ | 1 ВАРИАНТ | 2 ВАРИАНТ | 3 ВАРИАНТ | 4 ВАРИАНТ |
Do …. Loop | Do …. Loop While | Do …. Loop Until | Do While …. Loop | Do Until …. Loop |
Операторы вида For встречаются в 2 вариантах. О них – в следующем разделе.
8.3.1. Оператор Do …. Loop
Попытаемся составить с использованием 0 варианта оператора Do программу решения задачи о бесконечной печати чисел 3 5 7 … из предыдущего подраздела. Для того, чтобы точно определить работу этого варианта оператора Do, приведем ее параллельно с 0 вариантом программы решения этой задачи из того же подраздела. Работают эти параллельные варианты абсолютно одинаково. При этом объяснением любого оператора в правом столбце является оператор, стоящий в той же строчке в левом столбце.
0 ВАРИАНТ | 0 ВАРИАНТ ОПЕРАТОРА Do |
Write("Начало счета ") | Write("Начало счета ") |
f = 3 | f = 3 |
m: | Do |
Write(f & " ") | Write(f & " ") |
f = f + 2 | f = f + 2 |
GoTo m | Loop |
Doможно перевести, как «Делай», а понимать следует просто как метку.
Loopможно перевести, как «Петля» или «Возврат назад», а понимать следует так: «Возвращайся к метке Do».
Порядок работы обеих программ совершенно одинаков, так что можно считать слово Do заменой метки m:, а слово Loop считать заменой оператора GoTo m. Обе программы бесконечно печатают 3 5 7 9 11 …..
Общий смысл оператора Do такой: выполни по порядку сверху вниз все операторы между словами Do и Loop, затем выполни их по порядку еще раз и еще раз и так далее.
Толку в 0 варианте оператора Do мы из-за зацикливания видим мало (пока!).
Синтаксис оператора Do …. Loop:
Do
операторы
операторы
…………….
Loop
Строки операторов между Do и Loop называются телом цикла.
8.3.2. Оператор Do …. Loop While
Добавьте в ваш проект еще 4 кнопки и выполните в пошаговом режиме программы с вариантами оператора Do 1 – 4, которые я привел ниже. Вы увидите, что все 4 варианта делают одно и то же и они очень похожи. Вопрос о том, зачем нужно целых 4 похожих варианта, рассмотрим чуть позже. Уверяю, они все нужны.
Составим с использованием 1 варианта оператора Do программу решения задачи о печати чисел 3 5 7 9 из предыдущего подраздела. Для того, чтобы точно определить работу этого варианта оператора Do, приведем ее параллельно с 1 вариантом программы решения этой задачи из того же подраздела. Объяснением любого оператора в правом столбце является оператор, стоящий в той же строчке в левом столбце.
1 ВАРИАНТ | 1 ВАРИАНТ ОПЕРАТОРА Do |
Write("Начало счета ") | Write("Начало счета ") |
f = 3 | f = 3 |
m: | Do |
Write(f & " ") | Write(f & " ") |
f = f + 2 | f = f + 2 |
If f <= 9 Then GoTo m | Loop While f <= 9 |
Write("Конец счета") | Write("Конец счета") |
Whileпереводится «Пока». Значит, Loop While f <= 9 понимать следует так: «Возвращайся к метке Do, пока f<=9».
Порядок работы обеих программ совершенно одинаков, так что можно считать слово Do заменой метки m:, а конструкцию Loop While f <= 9 считать заменой оператора If f <= 9 Then GoTo m.
Синтаксис оператора Do …. Loop While:
Do
операторы
операторы
…………….
Loop While условие продолжения работы цикла
8.3.3. Оператор Do …. Loop Until
2 ВАРИАНТ | 2 ВАРИАНТ ОПЕРАТОРА Do |
Write("Начало счета ") | Write("Начало счета ") |
f = 3 | f = 3 |
m1: | Do |
Write(f & " ") | Write(f & " ") |
f = f + 2 | f = f + 2 |
If f > 9 Then GoTo m2 Else GoTo m1 | Loop Until f > 9 |
m2: Write("Конец счета") | Write("Конец счета") |
Untilпереводится «До тех пор, пока не».
Значит, Loop Until f > 9 понимать следует так: «Возвращайся к метке Do до тех пор, пока не выполнится условие f > 9».
Синтаксис оператора Do …. Loop Until:
Do
операторы
операторы
…………….
Loop Until условие завершения работы цикла
8.3.4. Оператор Do While …. Loop
3 ВАРИАНТ | 3 ВАРИАНТ ОПЕРАТОРА Do |
Write("Начало счета ") | Write("Начало счета ") |
f = 3 | f = 3 |
m1: If f <= 9 Then GoTo m3 Else GoTo m2 | Do While f <= 9 |
m3: Write(f & " ") | Write(f & " ") |
f = f + 2 | f = f + 2 |
GoTo m1 | Loop |
m2: Write("Конец счета") | Write("Конец счета") |
Do While f <= 9 понимать следует так: «Пока f <= 9, выполняй нижестоящие операторы вплоть до Loop».
Синтаксис оператора Do While …. Loop:
Do While условие продолжения работы цикла
операторы
операторы
…………….
Loop
8.3.5. Оператор Do Until …. Loop
4 ВАРИАНТ | 4 ВАРИАНТ ОПЕРАТОРА Do |
Write("Начало счета ") | Write("Начало счета ") |
f = 3 | f = 3 |
m1: If f > 9 Then GoTo m2 Else GoTo m3 | Do Until f > 9 |
m3: Write(f & " ") | Write(f & " ") |
f = f + 2 | f = f + 2 |
GoTo m1 | Loop |
m2: Write("Конец счета") | Write("Конец счета") |
Do Until f > 9 понимать следует так: «Выполняй нижестоящие операторы вплоть до Loop, до тех пор, пока не выполнится условие f > 9».
Синтаксис оператора Do Until …. Loop:
Do Until условие завершения работы цикла
операторы
операторы
…………….
Loop
8.3.6. Разница между вариантами операторов Do
Разницы две:
· Между While и Until. Здесь соображения удобства. Что вам удобнее: указывать компьютеру, когда цикл нужно продолжать (f <= 9) или когда его нужно заканчивать (f > 9)?
· В том, куда поставить условие – после Do или после Loop. Здесь разница не только в удобстве. В первом случае можно придумать такое условие, когда тело цикла не выполнится ни разу. Например,
Dim f As Integer = 3
Do Until f > 0
Debug.Write(f)
f = f - 10
Loop
Во втором случае, каково бы ни было условие, тело цикла хотя бы раз, да выполнится, потому что добраться до стоящего внизу условия можно только пройдя все стоящие выше него операторы.
Часто эти отличия для начинающих малосущественны, поэтому пока выбирайте вариант по вкусу.
Типичная ошибка начинающих – небрежное обращение со знаками сравнения. Многие не видят большой разницы в том, как записать – While f<=9 или While f<9, а затем, «недополучив» результат, удивляются, куда он делся. Если вы не понимаете, куда, попробуйте ошибочный вариант программы с While f<9 выполнить в пошаговом режиме.
Примеры и задания
Задание 29.
Выполнить с использованием оператора Do задачу из 8.2: Напечатать пары чисел – 0 1000 1 999 2 998 . . . . . . 1000 0. Напишите два варианта программы с использованием 1 и 2 вариантов оператора Do.
Задание 30.
Выполнить с использованием оператора Do Задание 43: Изображение, пройдя немного слева направо, поворачивает вниз и, пройдя немного, через некоторое время останавливается. Используйте 3 и 4 варианты оператора Do.
Задача: Компьютер предлагает человеку ввести слово, после чего распечатывает это слово, снабдив его восклицательным знаком. Затем снова предлагает ввести слово и так до тех пор, пока человек не введет слово «Хватит». Распечатав его с восклицательным знаком, компьютер отвечает «Хватит так хватит» и заканчивает работу.
Придумаем строковую переменную, в которую человек будет с клавиатуры вводить слово. Назовем ее Slovo. Выберем подходящий вариант оператора Do, это будет 2-й вариант (а 3-й и 4-й здесь вообще не подойдут), и пишем программу:
Dim Slovo As String
Do
Slovo = InputBox("Введите слово")
Debug.WriteLine(Slovo & "!")
Loop Until Slovo = "Хватит"
Debug.WriteLine("Хватит так хватит")
Задание 31.
Усложним эту задачу. Пусть компьютер перед распечаткой каждого слова ставит его порядковый номер. И еще: если слово длинней 10 букв, компьютер должен добавить – «Тяжелая жизнь.».
Задание 32.
«Полет камня». Это непростое задание разделим на два этапа:
Вычислительная часть. Если камень бросить горизонтально со 100-метровой башни со скоростью v=20м/с, то его расстояние от башни по горизонтали s будет выражаться формулой s=vt, где t – время полета камня в секундах. Высота над землей h будет выражаться формулой h=100-9.81t2/2. Требуется вычислять и печатать t, s и h для значений t = 0, 0.2, 0.4, 0.6 и так далее до тех пор, пока камень не упадет на землю.
Графическая часть (выполнять только в том случае, если получилась вычислительная). Нарисовать землю, башню и траекторию камня (Рис. 8.1). Указание: Траектория – серия кружочков. За одну итерацию цикла рисуется один кружочек. Высота башни – 100 пикселей. И в остальном тоже выберем масштаб – 1 метр – 1 пиксель, что удобно. Горизонтальная координата кружка на форме – это s с небольшим сдвигом вправо, так как бросаем не от левого края формы. Вертикальная координата кружка на форме – это 100-h с небольшим сдвигом вниз, так как бросаем не от верхнего края формы. Переменная h взята со знаком минус потому, что вертикальная ось в компьютерной системе координат направлена вниз.
Рис. 8.1
В Задание 104 мы изобразим полет камня в реальном времени.
Оператор Exit Do
Оператор Exit Doнужен для того, чтобы выходить из цикла не в начале тела цикла, как в вариантах 3-4, не в конце, как в вариантах 1-2, а в середине. Добавим, например, Exit Do в тело цикла одного из вариантов предыдущей программы:
Write("Начало счета ")
f = 3
Do
Write(f & " ")
Exit Do
f = f + 2
Loop While f <= 9
Write("Конец счета")
Вот результат работы этой программы:
Начало счета 3 Конец счета
Как видите, Exit Do – это всего лишь приказ перейти к оператору, следующему за словом Loop. Толк от Exit Do будет тогда, когда его поместят внутрь оператора ветвления:
Write("Начало счета ")
f = 3
Do
Write(f & " ")
If f >= 5 Then Exit Do
f = f + 2
Loop While f <= 9
Write("Конец счета")
Вот результат работы этой программы:
Начало счета 3 5 Конец счета
Пока в Exit Do особого смысла вы не видите. А увидите вы его в следующей задаче, где не подойдет ни один из вариантов 1 – 4 оператора Do, а подойдет Exit Do в совокупности с 0 вариантом.
Задача. Выполнить с использованием Do и Exit Do Задание 41: Для х=2700, 900, 300, 100 . . . и т.д. вычислять и печатать y=x/4+20 и z=2y+0.23 до тех пор, пока yz не станет меньше 1/х.
Загляните в ответ – как решена задача без Do. А вот как – с Do.
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim x, y, z As Double
x = 2700
Do
y = x / 4 + 20
z = 2 * y + 0.23
If y * z < 1 / x Then Exit Do
Debug.WriteLine(Format(x, "0.000000") & " " & Format(y, "0.000000") & " " & Format(z, "0.000000"))
x = x / 3
Loop
End Sub
Как видите, очень похоже. Обратите внимание, что если вы захотите обойтись без Exit Do и использовать один из вариантов 1 – 4 оператора Do, то у вас при использовании 1-2 вариантов «новый» x в условии y*z<1/x будет сравниваться со «старыми» y и z (в нашем конкретном случае это не приведет к катастрофе, но все равно это нехорошо). А использование 3-4 вариантов вообще невозможно без неоправданного усложнения программы.
Задание 33.
Решить чуть измененную задачу про «Хватит так хватит» из предыдущего раздела: Компьютер предлагает человеку ввести слово, после чего распечатывает это слово, снабдив его восклицательным знаком. Затем снова предлагает ввести слово и так до тех пор, пока человек не введет слово «Хватит». Его он не распечатывает вообще, а отвечает «Хватит так хватит» и заканчивает работу. Указание: Используйте Exit Do в совокупности с 0 вариантом Do. Остальные варианты не подойдут или подойдут плохо.
8.3.9. Оператор цикла While …End While
Имеется оператор цикла While …… End While, привычный для других языков и для предыдущих версий Бейсика. Вот вам его синтаксис:
While условие продолжения работы цикла
операторы
операторы
…………….
End While
А смысл – абсолютно тот же, что и у Do While …. Loop
Оператор цикла For
Я говорил, что имеются две разновидности оператора For. Здесь я рассмотрю только одну. Вторая будет разобрана в 16.2.2.
Вспомните программу печати чисел 3 5 7 9:
f = 3
Do
Write(f & " ")
f = f + 2
Loop While f <= 9
Здесь было выполнено 4 итерации цикла Do или, как еще говорят, цикл выполнился 4 раза, или, как говорят уже не очень правильно, было выполнено 4 цикла. Но обычно, когда мы пишем операторы Do, нам совсем не интересно знать, сколько итераций будет сделано.
Тем не менее, существует множество задач, для решения которых цикл нужно выполнить именно определенное количество раз. В этом случае вместо оператора Do удобнее использовать оператор цикла For.