Циклы - общее понятие; цикл с параметром
Автор: |
© Ерёмин А.А., 2007 |
Повторять следует только неповторимое! |
Номер урока: |
16 |
Введение
Запись последовательных команд не всегда может быть эффективным способом достижения поставленной цели. Очень часто в программе требуется выполнить одну и ту же последовательность действий несколько раз. Например, требуется вывести в текстовое поле (TMemo) числа от 1 до 100. Что делать в этом случае? Писать 100 строк кода? Конечно нет, это было бы просто глупо! Примеров можно привести множество. Причём во многих случаях число повторений может быть заранее неизвестно - в этом случае записать фиксированный набор команд просто невозможно.
На помощь придут циклы. Цикл - это специальная конструкция языка, позволяющая запрограммировать многократное выполнение определённого блока команд. Каждый "проход" цикла называется итерацией. В языке Pascal существует три типа циклов - цикл с параметром (цикл по переменной), цикл с предусловием и цикл с постусловием. В данном уроке мы познакомимся с циклом по переменной.
Цикл с параметром
Цикл с параметром (другое его название - цикл по переменной) позволяет выполнить набор команд фиксированное число раз, т.е. число итераций должно быть известно до начала выполнения цикла. Особенностью данного цикла является то, что заводится специальная переменная-счётчик, которая последовательно проходит указанный диапазон значений. Значение этой переменной может быть использовано в блоке кода, находящемся в цикле.
Цикл с параметром описывается зарезервированным словом FOR (англ. "для"). Общий вид конструкции цикла FOR:
FOR переменная-счётчик := начальное_значение [TO / DOWNTO] конечное_значение DO {Действия}
Переменная-счётчик - объявленная выше переменная перечислимого типа (в большинстве случаев - число).
Начальное значение и конечное значение - границы диапазона, который последовательно "пробежит" переменная-счётчик. Значения, естественно, того же типа данных, что и переменная-счётчик.
При этом следует обращать особое внимание на указанные значения. В зависимости от их соотношения используется либо ключевое слово TO, либо DOWNTO. Слово TO используется в том случае, когда конечное значение больше начального, а DOWNTO - наоборот, т.е. когда цикл пойдёт по убыванию. В случае, если указанные значения не будут соответствовать ключевому слову, то требуемый результат не будет достигнут - цикл, вероятно, выполнится всего один раз.
В качестве действий указывается какая-либо команда, либо набор команд. Если команд несколько, их, как обычно, следует заключать в блок BEGIN .. END.
Пример №1
Рассмотрим пример, речь о котором шла в начале статьи, только немного усложним его - будем выводить не только числа от 1 до 100, но и их квадраты.
Итак, мы заранее знаем, что нам следует выполнить одну и ту же команду - добавление строки в Memo, 100 раз. Запрограммируем это с помощью цикла FOR. Для начала следует разместить на форме TMemo (Memo1). Выполнение команд логичнее всего "повесить" на нажатие кнопки. Для добавления строк в TMemo следует воспользоваться методом Add его свойства Lines. Lines - это набор всех строк TMemo, а метод Add позволяет добавить указанную строку. Перед выполнением цикла содержимое Memo очищается.
procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin Memo1.Lines.Clear; for i := 1 to 100 do Memo1.Lines.Add(IntToStr(i)+': '+IntToStr(Sqr(i))) end; |
Разберём, что здесь происходит: мы заводим переменную i (Integer, целое число) и указываем её как счётчик для цикла. Поскольку нам нужно вывести числа от 1 до 100, указываем соответствующий диапазон. Команда - добавление строки в Memo, содержащей текущее число и его квадрат. Преобразование типов (в данном случае - чисел в строки) обязательно. Как это будет работать: сначала переменной i будет присвоено значение 1, после чего выполнится указанная команда, т.е. в Memo добавится строка "1: 1", далее i станет равным 2 и снова команда повторится. Так произойдёт 100 раз - в результате в Memo и окажется 100 строк.
Замечание
Одна из часто встречающихся ошибок - использование значения переменной-счётчика после завершена цикла. После выполнения цикла значение переменной-счётчика не определено! В данном случае после завершения цикла значение i не будет 100, хотя по случайности оно может быть. Если Вы хотите использовать значение переменной далее, присвойте ей это значение явным образом.
Вложенные циклы
Что такое вложенные циклы, понятно из названия - это циклы, которые вложены в другие циклы. К примеру, если один цикл позволяет вывести лишь ряд чисел, то 2 цикла, один из которых вложен в другой, позволят вывести целую таблицу значений.
Никаких специальных конструкций для вложенных циклов нет. Всё работает точно также. Переменные-счётчики циклов, как правило, называют буквами I, J, K, хотя название, конечно, может быть любое.
Пример №2
Простейший пример применения вложенного цикла - вывод таблицы умножения. Для начала продумаем алгоритм: для вывода таблицы для одного конкретного числа (например, для 5), нужно создать цикл, который пройдёт значения от 1 до 9 и выведет произведение числа 5 на каждое из этих чисел. А чтобы вывести таблицу для самих чисел от 1 до 9, нужен ещё один такой же цикл.
procedure TForm1.Button1Click(Sender: TObject); var i,j: Integer; begin Memo1.Lines.Clear; for i := 1 to 9 do for j := 1 to 9 do Memo1.Lines.Add(IntToStr(i)+' x '+IntToStr(j)+' = '+IntToStr(i*j)) end; |
Прерывание и продолжение цикла
Примечание: то, о чём пойдёт речь далее, применимо не только к циклу по переменной, но и к циклам с пред- и постусловием.
Не всегда выполнение фиксированного числа итераций приводит к нужному результату. Иногда в процессе выполнения могут возникнуть ситуации, когда цикл логично было бы завершить, не выполняя его до конца, т.е. нужно просто исключить все дальнейшие итерации. Такая возможность существует - для этого необходимо вызвать команду BREAK. Данная команда завершает цикл, который выполняется в данный момент, и продолжает выполнение программы. При этом текущая итерация не выполняется до конца - прерывание происходит именно в той строке, где указана команда Break.
Замечание: если цикл, выполнение которого прерывается командой Break, вложен в другой цикл, то "внешний" цикл продолжит своё выполнение, т.е. команда Break останавливает только один цикл, а не все имеющиеся.
Завершение цикла - экстренный метод. Иногда же нужно просто пропустить текущую итерацию и перейти к следующей. Вручную это можно сделать, заключив все команды в блок условного оператора, однако такой способен неудобен и только загромождает код. Именно поэтому существует команда продолжения цикла и называется она совершенно логично - CONTINUE. Эта команда "заставляет" цикл тут же перейти к следующей интерации, не продолжая выполнение текущей.
Пример №3
Данный пример призван продемонстрировать применение команды Break.
Задача. Определить, есть ли среди букв английского алфавита (A - Z, в верхнем регистре) такие символы, коды которых обладают следующим свойством: квадрат кода символа больше числа 5000. Если такие символы существуют, указать первый из них согласно алфавитному порядку.
Во-первых, следует определиться с циклом. Смвольный тип (Char) - перечислимый, значит его можно использовать для переменной-счётчика цикла For. Во-вторых, для определения кода символа служит функция Ord(). В-третьих, нам не обязательно просматривать абсолютно все символы - если будет найден хотя бы один, значит требуемое условие выполнено и этот символ мы должны вывести как результат работы. Просматривать остальные символы не требуется.
Опишем цикл, который последовательно пройдёт все символы от A до Z. Коды символов упорядочены согласно следованию соответстующих букв в алфавите, поэтому "пробег" произойдёт точно по алфавиту, для этого в коде не требуется дополнительно что-либо писать. Код будет приблизительно таким:
procedure TForm1.Button1Click(Sender: TObject); var c: Char; begin for c := 'A' to 'Z' do if Sqr(Ord(c)) > 5000 then begin ShowMessage('Символ: '+c+' (код: '+IntToStr(Ord(c))+', квадрат: '+IntToStr(Sqr(Ord(c)))+')'); Break end end;
Как сработает этот цикл: сначала будет взят символ "A", будет проверен код этого символа, после этого будет взят символ "B", проверен его код и т.д. Когда условие выполнится (это будет на символе "G"), будет выведено сообщение с результатом, но после этого цикл не продолжится - он тут же завершится. Мы уже нашли искомый символ, у нас есть результат, - зачем же тратить время на проверку остальных символов? В данном случае задержка по времени будет практически незаметна, ведь в алфавите всего 26 символов. Но что будет, если обрабатывать несколько тысяч записей?
Цикл с шагом
Цикл с шагом позволяет указать шаг прохода счётчиком указанного диапазона. Например, в случае, когда требуется пройти все числа от 0 до 1000, которые делятся на 10, шагом будет число 10, а границами диапазона - 0 и 1000. Цикл при этом будет выполняться таким образом: сначала будет взято первое число и выполнена соответствующая итерация, затем к счётчику будет прибавлена не 1, как обычно, а шаг, т.е. 10 - следующая итерация выполнится при значении счётчика, равном 10, далее - 20, 30 и т.д.
Однако есть одна проблема - в Pascal возможности задавать шаг... нет! Да, так уж сложилось, что этой конструкции не предусмотрено. В некоторых языках программирования она есть (например, в Basic), в некоторых - нет.
Однако не составляет труда написать небольшую надстройку над обычным циклом, которая позволит это сделать. Этот пример также продемонстирует использование команды Continue.
Всё просто - нам достаточно проверить, отстаёт ли текущее значение счётчика на целое число шагов от начального значения и, если да, то выполнить текущую итерацию, а если нет - продолжить цикл.
procedure TForm1.Button1Click(Sender: TObject); var i,First,Last,Step: Integer; begin First:=3; Last:=100; Step:=10; Memo1.Lines.Clear; for i := First to Last do if ((i - First) mod Step) <> 0 then Continue else Memo1.Lines.Add(IntToStr(i)) end;
Для тех, кто забыл: оператор mod выполняет деление с остатком. Разберёмся, как работает данный цикл: переменная-счётчик проходит значения указанного диапазона (в примере - от 3 до 100). Для проверки, является ли итерация "попадающей" под шаг, выполняется проверка, делится ли пройденное число единиц нацело на указанный шаг. В данном примере после 3 "попадание" будет на числе 13, т.к. 13 - 3 = 10 = Шаг * 1. Далее - 23, 33 и т.д. При этом первое значение диапазона всегда будет попадать в шаг, какое бы оно ни было, а последнее - не всегда (в данном случае 100 не попадёт).
Блок команд для выполнения в цикле в данном случае указывается после else.
Заключение
Сегодня мы познакомились с циклами, определили, для чего они нужны, а также познакомились с циклом по переменной - каким образом этот цикл записывается и как работает. Помимо этого мы разобрались, как организовать цикл с шагом, и как применять команды остановки и продолжения цикла. В следующий раз речь пойдёт о других циклах - циклах с постусловием и циклах с предусловием.
Автор: Ерёмин А.А.
Статья добавлена: 24 ноября 2007
Следующая статья: Обучающий курс. 17. Циклы - цикл с предусловием и цикл с постусловием »
Зарегистрируйтесь/авторизируйтесь,
чтобы оценивать статьи.
Статьи, похожие по тематике
- Обучающий курс. 10. Логические выражения
- Обучающий курс. 12. Функции и процедуры. Математические вычисления. Случайные числа
- Обучающий курс. 14. Выбор (ветвления)
- Обучающий курс. 17. Циклы - цикл с предусловием и цикл с постусловием
- Обучающий курс. 18. Работа со строками и символами
- Обучающий курс. 19. Подпрограммы (часть 1)
- Обучающий курс. 20. Подпрограммы (часть 2)
- Обучающий курс. 21. Подпрограммы (часть 3)
- Обучающий курс. 22. Простые типы данных
- Обучающий курс. 23. Множества
- Обучающий курс. 24. Записи (часть 1)
Для вставки ссылки на данную статью на другом сайте используйте следующий HTML-код:
Ссылка для форумов (BBCode):
Быстрая вставка ссылки на статью в сообщениях на сайте:
{{a:116}} (буква a — латинская) — только адрес статьи (URL);
{{статья:116}} — полноценная HTML-ссылка на статью (текст ссылки — название статьи).
Поделитесь ссылкой в социальных сетях:
Комментарии читателей к данной статье
Репутация: нет |
Аркадий (13 февраля 2016, 23:20): Господа, кто-нибудь может сказать почему с этим кодом:
//Поиск одинаковых строк >>>>>>>>>>>>>>>>> procedure TForm4.Button3Click(Sender: TObject); var i,i9,k,k9:Integer; p8,a,f8,e8:Integer; s10:String; C:array [0..10] of String; begin Windows.Beep(987,400); Windows.Beep(1975,400); i9:=Form4.Memo1.Lines.Count; Form4.Memo3.Lines.Clear; for i:= 0 to i9 - 1 do begin k9:=Form4.Memo2.Lines.Count; s10:=Form4.Memo1.Lines.Strings[i]; a:=0; for f8:= 0 to 10 do C[f8]:=''; for k:= 0 to k9 - 1 do begin p8:=Pos(s10,Form4.Memo2.Lines.Strings[k]); if p8>0 then begin C[a]:=Form4.Memo2.Lines.Strings[k]; Form4.Memo2.Lines.Delete(k); a:=a+1; end; end; if (C[0]>'')AND(C[1]>'') then for e8 := 0 to a-1 do Form4.Memo3.Lines.Add(C[e8]); end; Form4.Edit3.Text:=IntToStr(Form4.Memo3.Lines.Count)+' Совпадений'; end; На моём компьютере если совпадающие строки находятся подряд, пропускается вторая совпадающая строка? А на ноуте Пакардбэл у подруги ничего не пропускает, работает нормально? Это смахивает на то что цикл for k:= 0 to k9 - 1 do на ноуте считается в обратную сторону! Кто-нибудь сталкивался с этим? |
Репутация: +66 |
DNK (2 декабря 2013, 09:31) | 1 отзыв: Конечно можно!
Menu -> Project -> Project Options... -> Compiler -> Code Generation -> Optimization Вот только плохая идея, чтобы логика исполнения программы зависела от настроек компиляции. Можно собрать кучу граблей в будущем. |
Репутация: нет |
zvygin1964 (2 декабря 2013, 09:14): А оптимизацию кода у компилятора отключить можно?
|
Репутация: +66 |
DNK (28 ноября 2013, 15:55) | 1 отзыв: zvygin1964, тут акцент скорее на другом. Для кода:
for i := 1 to 100 do sum := sum + i; Memo1.Lines.Add(IntToStr(i)); - нельзя сказать однозначно, что будет выведено значение "100". Компилятор может, оптимизируя код, выдать перебор шагов цикла в обратном порядке (не единожды мной встречалось) или ещё более изощренные оптимизации. Поэтому если хотите использовать переменную-счетчик дальше, то лучше перед этим её инициализировать. В данном случае перед последней строкой нужно обязательно вставить строчку: i := 100; Это и называется "присвоить значение явным образом". |
Репутация: +40 |
Ерёмин А.А. (28 ноября 2013, 15:30): zvygin1964, да, только скобки не нужны.
|
Репутация: нет |
zvygin1964 (27 ноября 2013, 16:25): Вопрос о определении: "присвоить значение явным образом", то есть я написал правильно?
y:=(i); Это не о цикле, а о формулировке (она часто встречается в других уроках). |
Репутация: +40 |
Ерёмин А.А. (26 ноября 2013, 23:58) | 1 отзыв: Если есть цикл «for i:=1 to 100 do», то никто не гарантирует, что после его завершения i будет равно 100. Если это 100 используется в дальнейшей логике программы, то нужно явно написать i:=100.
|
Репутация: нет |
zvygin1964 (26 ноября 2013, 15:03): Мне не понятна формулировка в примере № 1 "Если Вы хотите использовать значение переменной далее, присвойте ей это значение явным образом".
Это делается так?: var i: Integer; y: Integer;//для дальнейшего использования begin Memo1.Lines.Clear; Memo2.Lines.Clear; for i := 1 to 100 do begin y:=(i); Memo1.Lines.Add(IntToStr(i)+': '+IntToStr(Sqr(i))) end; Memo2.Lines.Add(IntToStr(y)) //После выполнения цикла значение переменной-счётчика не определено! //Если Вы хотите использовать значение переменной далее, присвойте ей это значение явным образом. end; end. |
Репутация: нет |
IronDelphi (18 сентября 2011, 20:58): "Цикл на самом деле не заканчивается, а переходит на следущую итерацию, что и требуется. Если бы поставлен был оператор >>>break<<< -- то тогда бы цикл завершился."
Ну да, это верно. Выбрал не ту формулировку :) |
Репутация: нет |
Никонов Алексей (13 сентября 2011, 09:41): IronDelphi, читай эту статью лучше!
>>>Цикл с шагом: if ((i - First) mod Step) <> 0 then, здесь должно быть не неравенство <> 0 а наоборот равенство = 0, так как оператор mod возвращает остаток от деления нацело, а он должен быть равен нулю для соблюдения шага, а если нулю он не равен, значит достигнут необходимый результат, и цикл должен закончиться.<<< Цикл на самом деле не заканчивается, а переходит на следущую итерацию, что и требуется. Если бы поставлен был оператор >>>break<<< -- то тогда бы цикл завершился. |
Репутация: нет |
IronDelphi (14 мая 2011, 12:39): Статьи на самом деле очень хорошие, но в 16 уроке есть одно "но". Цикл с шагом: if ((i - First) mod Step) <> 0 then, здесь должно быть не неравенство <> 0 а наоборот равенство = 0, так как оператор mod возвращает остаток от деления нацело, а он должен быть равен нулю для соблюдения шага, а если нулю он не равен, значит достигнут необходимый результат, и цикл должен закончиться.
|
Репутация: +40 |
Ерёмин А.А. (9 августа 2010, 18:32): Устал — отдохни. В комментах-то зачем флудить?
|
Репутация: +1 |
antoca (9 августа 2010, 18:09): Чё-то устал я.
|
Репутация: +40 |
Ерёмин А.А. (15 марта 2009, 18:35): Не стоит так сильно волноваться - от опечаток никто не застрахован. В 14-ом уроке опечатка исправлена.
|
Репутация: нет |
Dj_smart (15 марта 2009, 15:40): Норм, так всё гуд, разве что в предыдущей статье огорчили крапаль.
|
Оставлять комментарии к статьям могут только зарегистрированные пользователи.