Здраствуйте, те кто читает эту
рассылку!
Имеем счастье доложить - программа
минимум уже выполнена! Читата из первого
сообщения в гостевой книге нашего сайта
гласит: " Я знаю много рассылок по
программированию. И большенство из них
умирали не доходя до циклов... " Этот
выпуск посвящён циклам. Так что
назначайте теперь программу максимум :)
Написать её можно на мыло
или на сайте там же можно взять
предыдущие выпуски рассылки.
В прошлый раз было голосование -
результаты в том же разделе.
Цикл записывается так for
переменная_цикла := начальное_значение to
конечное_значение do
операторы;
В нашем примере мы взяли в качестве
переменной цикла взята переменная i,
начальное значение 1, конечное - 20. Что
это значит? А это значит, что цикл будет
выполняться для i = 1, 2, 3, 4, 5 ... 19, 20. Т.е. 20
раз. Каждый раз мы прибавляем к i еденицу.
При этом начальное и конечное значение
переменной цикла может задаваться
численным выражением (например for i := 1 to 3*65
do ....) или выражением с переменной (например
for i := X + Y to X * Y / Z + 45 *j do ....). При этом эти
значения вычисляются компилятором один
раз перед выполнением цикла. Например,
результат такой программы:
Program Test;
var
i : integer;
x : integer;
begin
x := 10;
for i := 0 to х dobegin
writeLn (i, ' ', x);
x := x + 2
endend.
Как видите предел i не поменялся, хотя х
мы меняем! Использую цикл for нужно
соблюдать следующее правило: не нужно
менять переменную цикла (т.е. ту
переменную, по которой идёт цикл, у нас
это была i) внутри него.
Есть ещё один альтернативный вариант
цикла for, который используется, когда
считать нужно не "снизу вверх" а "сверху
вниз". Например мы хотим изменять i не
от 1 до 20, а наоборот от 20 до 1. Тогда наш
цикл примет вид:
for i := 20 downto 1 do
writeLn ('Привет!');
Теперь i = 20, 19, ... 2, 1. А так всё
аннологично. Основное неудобство
состоит в том, что мы не можем задавать
закон изменения переменной цикла.
Однако и это не так страшно, как кажется
на первый взгляд.
Цикл WHILE
Цикл номер два: while! Этот цикл
называется циклом с предусловием.
Записывается он так: while условие do
оператор;
Такой цикл выполняется пока условие
истинно. И прекращается в противном
случае. Например такой цикл:
while 1 do
writeLn ('Привет!');
Приведёт к "зависанию" компьютера,
т.к. выражение 1 всегда истинно (не могут
же быть числа ложными). выйдти из этого
цикла можно нажатием клавиши Ctrl+Break (Ctrl+C)
- стандартный выход из ДОС программ.
Проверка истистинности условия
проводится как и в операторе if.
Давайте покажем, как оператором While
можно заменить for:
i := 1;
x := 10;
while i <= x dobegin
writeLn (i, ' ', x);
x := x + 1;
i := i + 1
end;
Попробуйте этот код... ну как эффект не
тот? :) Давайте разберёмся в чём дело. А
вся проблемма в том, что условие (максимальное
значение) для цикла for считается зарание
один раз заранее! А у нас оно всё время
меняется. Ок. Теперь посмотрите
результат программы: она
останавливается при i = 32 757. Давайте
посмотрим ещё один вариант:
i := 1;
x := 10;
while i <> x dobegin
writeLn (i, ' ', x);
x := x + 1;
i := i + 1
end;
Запустите и подождите немного.... СТОП!
А откуда там отрицательные числа?? Ведь
мы прибавляем каждый раз по 1 и 1, т.е. i и х
растут ???
Давайте разберёмся по подробнее. Что
из себя представляет числовая прямая?
Это окружность максимального радиуса, т.е.
радиуса бесконечность. Такая окружность
вырождается в прямую. Однако для целых
чисел (типа integer) эта "бесконечность"
известна - 32767 (помните выпуск Типы
данных?). Счечик i можно представить, как
точку, которая движется по этой
окружности. Когда i доходит до максимума,
то она становится отрицательной, т.е.
-32767!!
А помните предыдущий пример, где всё
останавливалось при x = 32 767. Почему так
вышло? Прибавляя к х = 32767 один что мы
получаем: x + 1 = -32767 !!!! А условие цикла у
нас стоит i <= x т.е. когда х = 32 767 i = 32 757,
далее х + 1 = -32 767 i + 1 = 32 758 т.е. условие i <= x
не выполняется! И поэтому мы выходим из
цикла!
Поэтому аккуратнее с максимальным
значение integer, оно не такое большое, как
может показаться!
Теперь напишем нормальный вариант
цикла for через цикл while:
i := 1;
x := 10;
Temp := x;
while i <= Temp dobegin
writeLn (i, ' ', x);
x := x + 1;
i := i + 1
end;
Здесь мы заранее посчиталь верхнюю
границу цикла в переменную temp и тем
самым всё работает правильно.
Ну и напоследок...
Ну вот мы подошли к последнему циклу.
Он называется цикл с постусловием, цикл: repeat-until
! Этот цикл в общем и целом анологичен while,
но есть два отличия. Вот как он
записывается: repeat
оператор until условие;
Итак его отличия от while:
самое важное: проверка условия
совершается после выполнения
оператора. Таким образом этот цикл обязательно
выполнится хотя бы один раз. В то время
как while, может и не выполняться ни разу.
не самое важное, но очень нужно
запомнить, что критерием прекращения
цикла является тот случай, когда
условие истинно, а если оно ложно,
то цикл продолжится! В то время как в
while абсолютно противоположная
ситуация.
Давайте же рассмотрим какой-нить
пример:
i := 10;
repeat
writeLn (i);
i := i - 1
until i = 0;
Этот цикл выполняется пока i не равно
нулю. Т.е. для значений i = 10, 9, 8 ..... 2, 1.
Обратите внимание, что мы не используем
операторы begin-end т.к. этот цикл доспукает
использовать в своём теле сколько
хочешь операторов в отличие от while и for.
Вот написал и вспомнил о том, что не
сказал, что такое тело цикла :( Вот
дырявая башка! Итак тело цикла - это та
последовательность операторов, которая
выполниется. Т.е. например все операторы
между repeat и until - это и есть тело цикла.
Программа
Итак сегодня мы продолжим изучать
Отладчик и напишем ещё одну программу.
Наша новая программа - факториал! Что
такое факториал? По определению
факториал числа n (обозначается n! ) n! = 1 * 2
* 3 *....* (n-1) * n - т.е. перемножение чисел от 1
до n. Итак вот программа:
Program Factorial;
uses CRT;
var
n, i : integer;
Result : longint;
begin
ClrScr;
Write ('Какое значение? ');
ReadLn (n);
Result := n;
for i := 0 to n dobegin
Result := Result * i
end;
writeLn ('Результат: ', Result);
ReadLn
end.
Давайте же помотрим результат!!!!
Запускаем вводим число... ЧТО ЭТО ЗА
НАФИГ??? Почему 0? ... запускаем вновь ...
опять 0! Досада. Видимо в программу
залезла логическая ошибка! Ну вот теперь
мы познаем всю мощь отладчика!
Итак строки:
begin
ClrScr;
Write ('Какое значение? ');
ReadLn (n);
Выглядят довольно понятно и ошибка
явно не в них. Так зачем же нам тратить
наше драгоценное время на их пошаговое
исполнение? Давайте сразу же перейдем к
следующей строке. Наведите на неё курсор
и нажмите Ctrl+F8 (меню Debug - > Add breakpoint) Она
подсветится красным цветом. Что же
такого чудесного мы сделали? А вот что:
мы добавили breakpoint (по-русски: брякпоинт,
бряк:) - точку остановки. Когда
исполнение программы дойдёт до этой
строчки, то мы сразуже перейдём в режим
по-шагового исполнения. Увидите список
бряков можно Debug -> Breakpoints.
Итак бряк есть, теперь нам нужно всё
время смотреть, что же у нас в переменной
Result. Этого можно добится двумя путями.
Добавить внутрь цикла строчку типа:
writeLn (Result);
Использовать отладчик.
Второе на мой взгляд несколько
предпочтительнее :) Давайте сделаем вот
что: выберем Debug -> Add Watch или Ctrl+F7 - в
появившемся диалоге укажем имя нашей
переменной: Result (в поле Watch expression)
Появится окно watches (если не появилось, то
Debug -> Watch). В нём вы увидите значение
переменной Result. Так же можно для
практики добавить туда и переменные i и n.
Так тепрь мы можем наблюдать, что же там
внутри и как меняются переменные.
Ну вот, теперь запускаем программу
(Ctrl+F9), вводим для примера 5 и сразу же
оказываемся внутри программы.
Заметьте, что у нас такие значения: Result =
0, i = 0, n = 5. Давим на F8 (не забыли что это
такое:) Result изменился с 0 на 5 (делаем
вывод: оператор := работает правильно,
ошибка не в нём :))))
Входим в цикл (давим F8)... Заметьте, что я
специально добавил в цикл
конструкцию begin-end (в неё нет надобности).
Я это сделал, что бы при отладке внутри
цикла у нас подсветка двигалась :) а
иначе она просто будет висеть на строчке:
Result := Result * i; и создаётся впечатление
остановки программы.
Итак проходим первый этап цикла (i = 0)
опа!! Вот оно! Result сразу же изменился на 0 !
Давайте посмотрим, что же будет дальше...
а дальше он так и останется 0. Можно
заметить, что мы умножаем Result на i, но
ведь в начале цикла i = 0 т.е. мы умножаем
на 0 и поэтому Result всегда 0! УРА! Нашли
ошибку! Прервём исполнение программы
(Ctrl+F2). Для устранения ошибки мы сделаем
следующее: в цикле i должно изменяться от
1 до n. Я надеюсь, что переписать
программу для вас не составит труда.
Итак исправим, уберём бряк (на той
строчке Ctrl+F8)
Запустим программу... ВОТ ЧЁРТ! Теперь
Result не 0 но очень уж большой!!!! В чём же
дело?? Давайте повторим всё заново:
поставим бряк и запустим её снова...
введём 5... Постойте ка а почему это у нас
Result в начале равен 5?? Ведь факториал это
произведение от 1 до n ??? Да вот же в чём
дело! Мы вначале присваиваем Result := n; А
надо так: Result := 1; Теперь всё нормально!
Давайте удалим всю отладочную
информацию: Очистим все бряки (Debug ->
Breakpoints . кнопка Clear All) Удалим все
просмоторщики (в окне watches правая кнопка
мыши из меню Clear All) и выполним программу
заново.
Ещё одна мелочь, которая не сразу
бросается в глаза: переменная Result у нас
типа longint, однако и этого не достаточно,
что бы вместить факториалы чисел больше
31! Как подсчитать факториал для любых
чисел мы поговорим попозже. Обязательно
поговорим!
Голосование
Итак в прошлый раз нам было интересно
узнать, что вы думаете по поводу нового
дизайна рассылки. Так вот только после
выхода выяснилось, что в почтовой
программе the Bat! некоторые места
отображаются некорректно. Поэтому лучше
чиать странику в Opere'e или ещё лучше в
Internet Explorer'e. Соответственно дизайн
рассылки некоторое время останется
неизменным, а потом мы чего-нибудь
придумаем получше. Кстате голосование
доступно до 5 ноября по этой ссылке.