Добавлен , опубликован

Программирование Корсаров: основы

Содержание:
Цикл - это повторяющееся выполнение набора действий.
В скриптах нам доступно два типа циклов - 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++. Это инкремент.
Справка:
Операции увеличения переменной на 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 при котором цикл не пройдёт проверку условия. Тогда программа пропустит тело цикла и пойдёт выполнять код, который написан после него.
Вполне возможна ситуация, когда тело цикла вообще ни разу выполнено не будет:
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, осуществляется в начале. А инкремент/декремент счётчика - после выполнения тела цикла.
Это очень удобно - нам не нужно объявлять отдельную переменную счётчик. Также мы застрахованы от того, чтобы забыть проставить инкремент и попасть в бесконечный цикл. Сам счётчик имеет область видимости только в пределах данного цикла.
Таким образом, где нужно прогнать определённое кол-во итераций - используется for. А где условия более сложные - while.

Операторы break и continue

Вы уже видели оператор break в связке с оператором switch, но он может использоваться и с циклами.
Оператор break приводит к завершению выполнения циклов for и while раньше времени:
for (int count = 0, count < 5, count++)
{
    if (count == 3)
    {
        break;
    }
    
    // какой-то код
}
Эта программа никогда не достигнет значения count == 4, потому что при достижении значения count == 3 работа цикла будет завершена оператором break.
Чаще всего оператор break используют для выхода из бесконечного цикла:
while (true)
{
    int a = 0;
    
    if (a > 2)
    {
        break;
    }
    
    a = rand(5);
}
true всегда будет возвращать истину, поэтому условие цикла будет выполняться всегда. То есть приведённый цикл - бесконечный. А условие выхода из него мы задаём в теле самого цикла при помощи связки операторов if и break.
Ранее я писал, что из цикла также можно выйти при помощи оператора 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 уже не выполнится.
Здесь есть нюанс - инкремент счётчика цикла 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.
ПРАВИЛО Нужно быть предельно внимательным к счётчикам и другим операциям, которые касаются выполнения условий цикла, когда используете оператор continue.

`
ОЖИДАНИЕ РЕКЛАМЫ...
Ответы (1)
24
nazarpunk, ниасилил. Умудрился приболеть посреди лета, голова еле соображает.
Скорее всего, допишу уже на след. выходных.
38
Вспомнился древний срач про
i = i++ + ++i
А результат зависит от компилятора
Ответы (3)
24
ScorpioT1000, знаешь, я решил проверить как это работает в корсарах.
ОСТОРОЖНО, ШОК-КОНТЕНТ
Открыть
int i = 1;
int a;

a = ++i;    // a == 1
int i = 1;
int a;

a = i++;    // a == 2
int i = 1;
int a;

a = --i;    // a == -1
int i = 1;
int a;

a = i--;    // a == 0
int i = 1;
int a;

a = i++ + ++i;  // а на этом выполнение функции прекращается
проведу ещё немного тестов и, видимо, пойду дополнять статью
24
ScorpioT1000, дополнительные тесты показали, что преинкремент и предекремент вообще не работают:
УНО
int i = 4;
int a;

a = ++i;    // a == 1
int i = 4;
int a;

a = i++;    // a == 5
int i = 4;
int a;

a = --i;    // a == -1
int i = 4;
int a;

a = i--;    // a == 3
ДОС
int i = -4;
int a;

a = ++i;    // a == 1
int i = -4;
int a;

a = i++;    // a == -3
int i = -4;
int a;

a = --i;    // a == -1
int i = -4;
int a;

a = i--;    // a == -5
38
avuremybe, адочек) но хороший материал для статьи
24
Статья обновлена и дополнена:
  • добавлен раздел с операторами break и continue
  • описаны нюансы работы инкремента (спасибо ScorpioT1000)
Чтобы оставить комментарий, пожалуйста, войдите на сайт.