Цикл - это повторяющееся выполнение набора действий.
В скриптах нам доступно два типа циклов - while и for. Они очень похожи по своему устройству, но применяются в разных ситуациях.
В скриптах нам доступно два типа циклов - while и for. Они очень похожи по своему устройству, но применяются в разных ситуациях.
Цикл while
Самым простым является цикл while. Его синтаксис очень похож на оператор if/else, который мы изучали на предыдущем уроке:
while (условие)
{
тело цикла;
}
Когда выполнение программы доходит до строки с ключевым словом while - обрабатывается условие. Если его значением является true (любое ненулевое значение), то тогда выполняется тело цикла.
Однако, в отличие от оператора if, после завершения выполнения тела цикла, управление возвращается обратно к while и процесс проверки условия повторяется. Если условие опять является true - тело цикла выполняется еще раз. Кстати, каждое выполнение тела цикла называется итерацией.
ref npc;
int i = 1;
while (i <= 3)
{
npc = characterFromID("saylor_0" + i);
LAi_SetCurHPMax(npc);
i++;
}
Рассмотрим детально этот кусок кода.
Во-первых, создаются переменные: i будет выступать счётчиком для нашего цикла, а npc это просто ссылка на персонажа.
Далее, при достижении строки while проверяется условие i <= 3. Поскольку мы инициализировали переменную i значением 1, то условие выполняется и программа выполняет стейтменты, описанные в теле цикла.
Это просто перебор персонажей с именами "saylor_01", "saylor_02", "saylor_03" и восстановление им здоровья.
А в конце стоит интересный стейтмент i++. Это инкремент.
Во-первых, создаются переменные: i будет выступать счётчиком для нашего цикла, а npc это просто ссылка на персонажа.
Далее, при достижении строки while проверяется условие i <= 3. Поскольку мы инициализировали переменную i значением 1, то условие выполняется и программа выполняет стейтменты, описанные в теле цикла.
Это просто перебор персонажей с именами "saylor_01", "saylor_02", "saylor_03" и восстановление им здоровья.
А в конце стоит интересный стейтмент i++. Это инкремент.
Справка:Операции увеличения переменной на 1 (инкремента) и уменьшения на 1 (декремента) настолько часто используемые, что у них есть свои собственные операторы в языке C++. Кроме того, каждый из этих операторов имеет две версии применения: префикс и постфикс.Префиксный инкремент сначала увеличивает значение переменной, а потом использует её в выражении. Постфиксный наоборот, сначала использует старое значение переменной для вычислений, а после этого увеличивает её значение на единицу. То же самое с декрементом.++x; // Префиксный инкремент (пре-инкремент) --x; // Префиксный декремент (пре-декремент) x++; // Постфиксный инкремент (пост-инкремент) x--; // Постфиксный декремент (пост-декремент)
Традиционно, здесь не обошлось без НЮАНСОВ
- Преинкремент и предекремент вообще не работают.
- Постинкремент и постдекремент работают как преинкремент и предекремент соответственно.
Рассмотрим пример:int a = 1; int b = 4; int c; c = b + a++;
В переменной c будет число 6.
По правилам С++, туда должно было попасть значение 5 поскольку мы использовали пост-инкремент. То есть, сначала значение переменной a должно быть использовано в вычислении выражения, а только после этого увеличиться на единицу.
Но особенности скриптовой части Корсаров таковы, что сначала вычисляется инкремент/декремент, а потом результат используется в выражениях.Что касается использования преинкремента. При использовании в файлах интерфейсов и диалогов:++i; // всегда возвращает 1 --i; // всегда возвращает -1
В остальных местах, в зависимости от сценария использования, код либо не отрабатывает, либо игра вылетает с ошибкой.Попытка использовать постинкремент и преинкремент в одном выражении вообще приводит к завершению работы ф-ции, в которой это было использовано.
Никаких ошибок мы при этом не получаем, а завершение ф-ции происходит конкретно на строке с невалидным кодом. Остальной код (после данной ф-ции) отрабатывает нормально.a = i++ + ++i;
Больше примеров есть в ответах под комментарием Scorpio.
Таким образом, в конце цикла мы увеличиваем значение i на единицу. Далее программа возвращается к началу цикла и снова проверяет условие. На этот раз i == 2, что все ещё соответствует условию и цикл выполняется ещё раз.
И так будет происходить до тех пор, пока i не достигнет значения 4 при котором цикл не пройдёт проверку условия. Тогда программа пропустит тело цикла и пойдёт выполнять код, который написан после него.
И так будет происходить до тех пор, пока i не достигнет значения 4 при котором цикл не пройдёт проверку условия. Тогда программа пропустит тело цикла и пойдёт выполнять код, который написан после него.
Вполне возможна ситуация, когда тело цикла вообще ни разу выполнено не будет:
int i = 7;
while (i < 3)
{
// ...
}
Возможна и обратная ситуация, когда условие всегда будет принимать значение true и программа никогда не выйдет из цикла. Это называется бесконечным циклом.
int i = 1;
while (i > 0)
{
// ...
i++
}
Единственный способ выйти из бесконечного цикла - использовать операторы return, break или функцию exit().
Существуют программы, которые преднамеренно работают в режиме бесконечного цикла (например веб-сервер, который непрерывно и постоянно обслуживает веб-запросы), но в 99.9% случаях, попадание в бесконечный цикл (если из него не предусмотрен выход через упомянутые операторы) - это ошибка программиста, которая ведёт к зависанию программы (ведь у вас перестает выполняться остальной код - программа застряла в одном цикле).
Цикл for
Дизайн цикла for подразумевает использование когда нужно прогнать цикл определённое кол-во раз. То есть for - это про счётчики, которые были в примере выше.
Давайте посмотрим, как он устроен:
Давайте посмотрим, как он устроен:
for (int i = 1, i < 4, i++)
{
npc = characterFromID("saylor_0" + i);
LAi_SetCurHPMax(npc);
}
В аргументах данного цикла находится немного больше элементов, чем мы видели ранее.
Первое, что мы видим - переменная-счётчик объявляется прямо там. После неё идёт условие. И затем инкремент либо декремент счётчика.
Первое, что мы видим - переменная-счётчик объявляется прямо там. После неё идёт условие. И затем инкремент либо декремент счётчика.
Выполняется данный цикл в точности так же, как и while. Только оформлен он так, что использовать его можно только со счётчиком. Тогда как в условие цикла while можно записать вообще что угодно (в точности как с оператором if/else).
На всякий случай уточню - проверка условия, как и в цикле while, осуществляется в начале. А инкремент/декремент счётчика - после выполнения тела цикла.
Это очень удобно - нам не нужно объявлять отдельную переменную счётчик. Также мы застрахованы от того, чтобы забыть проставить инкремент и попасть в бесконечный цикл. Сам счётчик имеет область видимости только в пределах данного цикла.
На всякий случай уточню - проверка условия, как и в цикле while, осуществляется в начале. А инкремент/декремент счётчика - после выполнения тела цикла.
Это очень удобно - нам не нужно объявлять отдельную переменную счётчик. Также мы застрахованы от того, чтобы забыть проставить инкремент и попасть в бесконечный цикл. Сам счётчик имеет область видимости только в пределах данного цикла.
Таким образом, где нужно прогнать определённое кол-во итераций - используется for. А где условия более сложные - while.
Операторы break и continue
Вы уже видели оператор break в связке с оператором switch, но он может использоваться и с циклами.
Оператор break приводит к завершению выполнения циклов for и while раньше времени:
Оператор break приводит к завершению выполнения циклов for и while раньше времени:
for (int count = 0, count < 5, count++)
{
if (count == 3)
{
break;
}
// какой-то код
}
Эта программа никогда не достигнет значения count == 4, потому что при достижении значения count == 3 работа цикла будет завершена оператором break.
Чаще всего оператор break используют для выхода из бесконечного цикла:
Чаще всего оператор break используют для выхода из бесконечного цикла:
while (true)
{
int a = 0;
if (a > 2)
{
break;
}
a = rand(5);
}
true всегда будет возвращать истину, поэтому условие цикла будет выполняться всегда. То есть приведённый цикл - бесконечный. А условие выхода из него мы задаём в теле самого цикла при помощи связки операторов if и break.
Ранее я писал, что из цикла также можно выйти при помощи оператора return.
Мы уже рассматривали этот оператор в уроке о функциях. Там я говорил, что данный оператор завершает работу функции.
Важно понимать эту разницу. Использовав в цикле оператор return вы завершите работу не только цикла, но и всей функции, в которой этот цикл находится. Никакой код, написанный ниже этого оператора, выполнен не будет.
Мы уже рассматривали этот оператор в уроке о функциях. Там я говорил, что данный оператор завершает работу функции.
Важно понимать эту разницу. Использовав в цикле оператор return вы завершите работу не только цикла, но и всей функции, в которой этот цикл находится. Никакой код, написанный ниже этого оператора, выполнен не будет.
Оператор continue позволяет сразу перейти в конец тела цикла, пропуская весь код, который находится под ним. Это используется когда мы хотим завершить текующую итерацию раньше времени.
string s;
for (int i = 1, i < 3, i++)
{
if (i == 2)
{
s = "yes";
continue;
}
s = "not";
}
По завершению данного цикла в переменной s будет находиться значение "yes".
Когда счётчик достигнет значения i == 2 мы присваиваем значение "yes" строковой переменной s и пропускаем остальной код тела цикла при помощи оператора continue.
После тела цикла выполняется инкремент счётчика и больше итераций выполнено не будет, т.к. условие i < 3 уже не выполнится.
Когда счётчик достигнет значения i == 2 мы присваиваем значение "yes" строковой переменной s и пропускаем остальной код тела цикла при помощи оператора continue.
После тела цикла выполняется инкремент счётчика и больше итераций выполнено не будет, т.к. условие i < 3 уже не выполнится.
Здесь есть нюанс - инкремент счётчика цикла for выполняется за пределами тела цикла. Но если мы напишем такую же программу, только с циклом while, ситуация может выйти из-под контроля:
int i = 1;
while (i < 3)
{
if (i == 2)
{
continue;
}
i++;
}
Когда значение счётчика достигает i == 2, мы проходим проверку оператора if и завершаем текущую итерацию. Таким образом, находящийся в конце тела цикла инкремент, выполнен не будет и значение переменной i так и останется равным 2.
Что происходит дальше? Да ничего необычного - выполняется проверка условия цикла i < 3, которую мы успешно проходим, т.к. i == 2. Далее снова проходим проверку оператора if и завершаем текущую итерацию оператором continue. Т.е. наш цикл стал бесконечным.
Чтобы всё работало как ожидается, мы должны были увеличить значение счётчика перед выполнением оператора continue.
Что происходит дальше? Да ничего необычного - выполняется проверка условия цикла i < 3, которую мы успешно проходим, т.к. i == 2. Далее снова проходим проверку оператора if и завершаем текущую итерацию оператором continue. Т.е. наш цикл стал бесконечным.
Чтобы всё работало как ожидается, мы должны были увеличить значение счётчика перед выполнением оператора continue.
ПРАВИЛО Нужно быть предельно внимательным к счётчикам и другим операциям, которые касаются выполнения условий цикла, когда используете оператор continue.
Скорее всего, допишу уже на след. выходных.
Ред. avuremybe
Ред. ScorpioT1000