Delphi.int.ru — Портал программистов

Вход Регистрация | Забыли пароль?

События

Сегодня:
Вопросы0    Ответы0    Мини-форумы0


Последние:
Вопрос20.07, 15:06 / #6671
Ответ30.06, 12:52 / #6666
Новости30 апреля 2012


Сейчас онлайн:
На сайте — 9
На IRC-канале — 1

Ссылки
Новый игровой клуб онлайн слотов - ссылка на сайт.

Циклы - общее понятие; цикл с параметром

Автор:
© Ерёмин А.А., 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 очищается.

Программа, выводящая квадраты чисел от 1 до 100
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. Циклы - цикл с предусловием и цикл с постусловием »

Рейтинг статьи: 4.80 Голосов: 10 Ваша оценка:

Зарегистрируйтесь/авторизируйтесь,
чтобы оценивать статьи.


Статьи, похожие по тематике

 

Для вставки ссылки на данную статью на другом сайте используйте следующий 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

на ноуте считается в обратную сторону!
Кто-нибудь сталкивался с этим?
DNK
Репутация: +64

DNK (2 декабря 2013, 09:31) | 1 отзыв:

Конечно можно!
Menu -> Project -> Project Options... -> Compiler -> Code Generation -> Optimization
Вот только плохая идея, чтобы логика исполнения программы зависела от настроек компиляции. Можно собрать кучу граблей в будущем.
zvygin1964
Репутация: нет

zvygin1964 (2 декабря 2013, 09:14):

А оптимизацию кода у компилятора отключить можно?
DNK
Репутация: +64

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
Репутация: нет

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
Репутация: нет

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
Репутация: нет

IronDelphi (18 сентября 2011, 20:58):

"Цикл на самом деле не заканчивается, а переходит на следущую итерацию, что и требуется. Если бы поставлен был оператор >>>break<<< -- то тогда бы цикл завершился."

Ну да, это верно. Выбрал не ту формулировку :)
Никонов Алексей
Репутация: нет

Никонов Алексей (13 сентября 2011, 09:41):

IronDelphi, читай эту статью лучше!

>>>Цикл с шагом: if ((i - First) mod Step) <> 0 then, здесь должно быть не неравенство <> 0 а наоборот равенство = 0, так как оператор mod возвращает остаток от деления нацело, а он должен быть равен нулю для соблюдения шага, а если нулю он не равен, значит достигнут необходимый результат, и цикл должен закончиться.<<<

Цикл на самом деле не заканчивается, а переходит на следущую итерацию, что и требуется. Если бы поставлен был оператор >>>break<<< -- то тогда бы цикл завершился.
IronDelphi
Репутация: нет

IronDelphi (14 мая 2011, 12:39):

Статьи на самом деле очень хорошие, но в 16 уроке есть одно "но". Цикл с шагом: if ((i - First) mod Step) <> 0 then, здесь должно быть не неравенство <> 0 а наоборот равенство = 0, так как оператор mod возвращает остаток от деления нацело, а он должен быть равен нулю для соблюдения шага, а если нулю он не равен, значит достигнут необходимый результат, и цикл должен закончиться.
Ерёмин А.А.
Репутация: +40

Ерёмин А.А. (9 августа 2010, 18:32):

Устал — отдохни. В комментах-то зачем флудить?
antoca
Репутация: +1

antoca (9 августа 2010, 18:09):

Чё-то устал я.
Ерёмин А.А.
Репутация: +40

Ерёмин А.А. (15 марта 2009, 18:35):

Не стоит так сильно волноваться - от опечаток никто не застрахован. В 14-ом уроке опечатка исправлена.
Dj_smart
Репутация: нет

Dj_smart (15 марта 2009, 15:40):

Норм, так всё гуд, разве что в предыдущей статье огорчили крапаль.

Оставлять комментарии к статьям могут только зарегистрированные пользователи.