Цикл for: рисуем ряды

Скопировать одну строку десять раз — скучно и хрупко. Цикл for делает это за тебя: ты пишешь рисование цыплёнка один раз, а на холсте выстраивается целый ряд.

Главное правило урока: если тебе нужно нарисовать много почти одинаковых фигур, не копируй код — заведи цикл for и сдвигай каждую копию по координате.

Зачем тебе цикл, а не копипаста

Представь, что наш CodeChick загрустил и захотел друзей. Один цыплёнок на холсте — это здорово, но скучновато. Давай нарисуем целый ряд цыплят, как стая на жёрдочке. В прошлом уроке про первые формы ты уже умеешь рисовать одного кружком и треугольником. Логичный первый порыв — взять и скопировать этот код пять раз, поменяв у каждого координату x.

Выглядит это примерно так:

circle(60, 200, 50);
circle(120, 200, 50);
circle(180, 200, 50);
circle(240, 200, 50);
circle(300, 200, 50);

Результат: пять одинаковых кружков выстроились в ряд слева направо по центру холста — наша первая «стая». Работает, но посмотри, сколько строк. А если цыплят должно быть не пять, а пятьдесят?

Тут и начинаются проблемы копипасты. Хочешь сделать цыплят чуть крупнее — придётся вручную поправить 50 в каждой из пяти строк, и не дай бог где-то опечататься. Захотел десять цыплят вместо пяти — дописывай ещё пять строк и сам считай координаты. Это как переписывать одно и то же сообщение пятидесяти друзьям вручную, вместо того чтобы кинуть его в общий чат один раз. Скучно, долго и легко ошибиться.

Компьютеры обожают делать одно и то же много раз — это их суперсила. И в любом языке программирования для этого есть специальный инструмент — цикл. К концу урока ты напишешь рисование цыплёнка один раз, а на холсте появится ровный ряд из скольких угодно копий — поменял одно число и получил стаю любого размера. Поехали разбираться, как устроен главный цикл-работяга по имени for.

Что такое цикл for и как его читать

Цикл — это команда «повтори вот этот кусок кода несколько раз». Представь рабочего на конвейере: ему говорят «возьми деталь, прикрути, отложи — и так для каждой из десяти деталей». Он не запоминает десять отдельных инструкций, он держит в голове одну и просто повторяет её, отсчитывая детали. Цикл for устроен ровно так же: внутри него сидит счётчик, который считает повторы.

Слово for переводится как «для». Можно прочитать команду буквально: «для каждого номера от нуля до пяти — сделай вот это». Ты наверняка уже встречал эту идею в жизни, даже не зная слова «цикл»: плейлист повторяет треки по списку, лента в соцсети рисует карточку за карточкой по одному шаблону, а игра расставляет одинаковых врагов по уровню. Везде под капотом сидит цикл, который берёт один рецепт и применяет его много раз подряд. Сейчас ты научишься писать такой рецепт сам.

Три части в скобках: старт, условие, шаг

У цикла for есть характерная «голова» — круглые скобки, в которых через точку с запятой записаны три части. Вот общий вид:

for (старт; условие; шаг) {
  // тело цикла — то, что повторяется
}

Разберём эти три части на пальцах. Это самое важное место урока, так что не спеши:

  • Старт — с какого числа начинаем считать. Здесь мы заводим переменную-счётчик, обычно её называют i (от слова index, «номер»). Например, let i = 0 значит «начни счёт с нуля». Эта часть выполняется один раз, в самом начале.

  • Условие — пока оно верно, цикл продолжает крутиться. Например, i < 5 значит «повторяй, пока i меньше пяти». Как только условие станет ложным, цикл останавливается.

  • Шаг — что сделать со счётчиком после каждого повтора. Чаще всего это i++ — короткая запись для «увеличь i на единицу». Именно шаг двигает счётчик вперёд, иначе цикл крутился бы вечно.

Прочитай вслух такую строку: for (let i = 0; i < 5; i++) — «начни с нуля, повторяй, пока меньше пяти, и каждый раз прибавляй один». Сколько раз выполнится тело? Считаем значения i: 0, 1, 2, 3, 4 — это пять штук. На i = 5 условие 5 < 5 уже ложно, и цикл стоп. Запомни этот фокус: i < 5 даёт ровно пять повторов, начиная с нуля.

Как счётчик ходит по кругу

Чтобы прочувствовать ритм цикла, разложим его по шагам. Возьмём вот такой простой цикл и проследим, что происходит:

for (let i = 0; i < 3; i++) {
  print(i);   // print выводит число в консоль
}

Результат: в консоли (это окошко для текстовых сообщений под холстом) появятся три числа подряд — 0, 1, 2. Холст при этом пустой: мы пока просто считаем, ничего не рисуя.

Проследим путь счётчика по шагам, как кадр за кадром:

  1. Старт: заводим i = 0.
  2. Проверка: 0 < 3? Да. Выполняем тело: печатаем 0.
  3. Шаг: i++ делает i равным 1.
  4. Проверка: 1 < 3? Да. Печатаем 1.
  5. Шаг: i становится 2.
  6. Проверка: 2 < 3? Да. Печатаем 2.
  7. Шаг: i становится 3.
  8. Проверка: 3 < 3? Нет! Условие ложно — цикл заканчивается.

Видишь круг? Проверил условие → выполнил тело → сделал шаг → снова проверил. Этот круг и называется итерацией. Самое полезное здесь — что на каждой итерации i хранит свой номер: сначала 0, потом 1, потом 2. И вот этот меняющийся номер мы сейчас превратим в координату, чтобы расставить цыплят по холсту.

Разбор на примерах: от точек к ряду цыплят

Пример 1: ряд кружков одним циклом

Помнишь те пять скопированных строк с кружками из начала урока? Заменим их одним циклом. Главная идея: координата x каждого кружка зависит от номера i.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(135, 206, 235);   // голубое небо
  fill(255, 209, 64);          // жёлтая заливка
  noStroke();

  for (let i = 0; i < 5; i++) {
    let x = 60 + i * 60;       // 60, 120, 180, 240, 300
    circle(x, 200, 50);
  }
}

Результат: те же пять жёлтых кружков ровным рядом по центру голубого холста — но теперь это сделано пятью строками цикла вместо пяти строк копипасты. Кружки стоят на одинаковом расстоянии 60 пикселей друг от друга.

Вся магия в строке let x = 60 + i * 60. Давай подставим номера и посмотрим, что получается:

ii * 60x = 60 + i * 60
0060
160120
2120180
3180240
4240300

Получились ровно те же координаты, что мы писали руками! Только теперь их вычисляет цикл. Здесь 60 в начале (60 +) — это отступ первого кружка от левого края, а * 60 — расстояние между соседними кружками, его называют шагом сетки. Поменяй i < 5 на i < 7 — и цыплят станет семь, без единой новой строки. Вот ради чего всё затевалось.

Пример 2: настоящий ряд цыплят

Кружок — это здорово, но мы обещали стаю CodeChick. Соберём в теле цикла полноценного цыплёнка: жёлтое тело, оранжевый клюв и тёмный глаз. Раз тело цикла повторяется, один и тот же набор фигур нарисуется для каждого i — на своём месте.

function setup() {
  createCanvas(480, 300);
}

function draw() {
  background(135, 206, 235);
  noStroke();

  for (let i = 0; i < 6; i++) {
    let x = 60 + i * 75;       // позиция текущего цыплёнка по горизонтали
    let y = 160;               // все стоят на одной линии

    // тело
    fill(255, 209, 64);
    circle(x, y, 60);

    // клюв
    fill(255, 140, 30);
    triangle(x + 22, y - 4, x + 45, y + 2, x + 22, y + 12);

    // глаз
    fill(40);
    circle(x + 10, y - 8, 8);
  }
}

Результат: шесть одинаковых жёлтых цыплят с оранжевыми клювами и тёмными глазами выстроились в ряд на голубом небе, как стая на проводе. Все смотрят вправо, расстояние между ними одинаковое.

Обрати внимание на самый важный приём: каждая координата фигуры завязана на x. Клюв рисуется в x + 22, глаз — в x + 10. Мы как бы говорим: «нарисуй клюв на 22 пикселя правее центра этого цыплёнка». А x на каждой итерации свой, поэтому весь цыплёнок целиком переезжает на новое место — вместе с клювом и глазом. Если бы мы написали клюв в фиксированном circle(82, ...), все клювы слиплись бы в одной точке. Запомни правило: всё тело объекта привязывай к одной базовой координате, которая зависит от i.

Пример 3: меняем размер стаи одним числом

Давай докажем, что цикл — это про гибкость. Вынесем количество цыплят в отдельную переменную и научимся управлять стаей, меняя одно-единственное число.

function setup() {
  createCanvas(480, 300);
}

function draw() {
  background(135, 206, 235);
  noStroke();

  let count = 8;                       // сколько цыплят в стае
  let gap = 440 / count;               // расстояние подгоняется под количество

  for (let i = 0; i < count; i++) {
    let x = 30 + i * gap;
    fill(255, 209, 64);
    circle(x, 160, gap * 0.7);         // размер тоже зависит от gap
    fill(255, 140, 30);
    triangle(x + gap * 0.25, 156, x + gap * 0.5, 160, x + gap * 0.25, 168);
  }
}

Результат: восемь жёлтых цыплят с клювами равномерно заполняют холст по ширине. Если поменять count на 4 — будет четыре крупных цыплёнка пошире, если на 12 — двенадцать мелких, и в обоих случаях они аккуратно поместятся, потому что gap и размер пересчитываются сами.

Вот это уже по-настоящему сильно. Мы связали и расстояние (gap = 440 / count), и размер тела (gap * 0.7) с количеством. Теперь стая сама подстраивается под холст: одно число count управляет всей картинкой. Поиграй с ним — это и есть тот самый дух творческого кодирования, когда меняешь параметр и смотришь, как перестраивается вся работа.

Заметь, как мы тут думали. Сначала спросили себя: «что в картинке должно меняться от количества?» — расстояние между цыплятами и их размер. Потом выразили обе величины через count формулами, а не вбили числами. Это очень полезная привычка: как только видишь в коде число, которое зависит от другого числа, лучше посчитать его формулой, чем вписать вручную. Тогда работа остаётся «живой» — её можно настраивать одним движением, не переписывая всё заново. Именно так художники-программисты делают свои генеративные постеры: меняют пару параметров и получают сотни вариантов одной идеи.

Частые ошибки и подводные камни

  • Забыл шаг i++ — и всё зависло. Если в третьей части не двигать счётчик, i навсегда останется нулём, условие i < 5 вечно истинно, и цикл крутится бесконечно. Браузер при этом замирает. Всегда проверяй, что счётчик меняется и со временем нарушит условие.

  • Все фигуры рисуются в одной точке. Классическая ловушка: координату забыли привязать к i. Если написать circle(200, 200, 50) внутри цикла, кружок нарисуется пять раз поверх самого себя — на холсте ты увидишь один. Координата обязана зависеть от i, например 60 + i * 60.

  • Промах на единицу: цыплят на одного больше или меньше. Это самая знаменитая ошибка новичков. i < 5 даёт пять повторов (0,1,2,3,4), а вот i <= 5 — уже шесть (0,1,2,3,4,5). Если стая не той длины — первым делом смотри на знак в условии: < или <=.

  • Цыплята убегают за край холста. Если шаг сетки слишком большой (i * 100 при холсте шириной 400), последние копии нарисуются за пределами видимой области и просто пропадут. Они нарисовались, ты их не видишь. Подгоняй шаг под ширину холста или, как в примере 3, вычисляй его из количества.

  • Объявил i вне цикла или перепутал имя. Счётчик удобно объявлять прямо в скобках через let i = 0 — тогда он живёт только внутри цикла и не мешает остальному коду. И следи, чтобы внутри тела ты обращался именно к i, а не к x или другой переменной — частая опечатка, когда координата вдруг не меняется.

Мини-практика: своя стая CodeChick

Возьми за основу пример 2 с рядом цыплят и преврати его в свою работу. Вот план по шагам:

  1. Сначала просто поменяй число повторов: сделай i < 10 и подбери шаг сетки так, чтобы десять цыплят красиво поместились на холсте. Почувствуй, как одно число управляет всей стаей.
  2. Добавь чередование: пусть каждый второй цыплёнок будет чуть выше остальных. Подсказка — внутри цикла можно проверить if (i % 2 === 0) (чётный ли номер) и для чётных взять другую координату y.
  3. Сделай так, чтобы цыплята постепенно росли слева направо: свяжи диаметр тела с номером, например 40 + i * 5. Получится ряд «от малыша к великану».
  4. Для смелых: добавь второй ряд цыплят выше первого — просто допиши ещё один цикл for с другим значением y. Так ты увидишь, что циклов в скетче может быть сколько угодно.

Меняй числа смело и смотри, что получается, — сломать тут ничего нельзя, а интуиция про циклы прокачивается именно так, руками.

Итоги

Ты познакомился с главным инструментом повторения в программировании и сразу применил его к делу:

  • Цикл for повторяет кусок кода много раз, избавляя от копипасты.
  • В скобках три части: старт (let i = 0), условие (i < 5) и шаг (i++); условие со знаком < даёт ровно столько повторов, сколько стоит справа.
  • На каждой итерации счётчик i хранит свой номер — и через формулу вроде 60 + i * 60 этот номер превращается в координату, расставляя копии по холсту.
  • Привязывай все части объекта к одной базовой координате, зависящей от i, иначе фигуры слипнутся в одной точке.

Теперь у CodeChick есть целая стая друзей, и нарисовал ты её одной формулой. Но ряд — это пока скучновато: всё ровно, всё одинаково. В следующем уроке мы научимся вкладывать один цикл в другой и из ряда сделаем настоящую сетку — стаю цыплят и вдоль, и поперёк, как клетки в тетради. Увидимся на следующей странице!

Проверьте себя
1. Сколько раз выполнится тело цикла for (let i = 0; i < 5; i++)?
A4 раза
B5 раз
C6 раз
DБесконечно
2. За что отвечает третья часть в скобках цикла for, например i++?
AЗадаёт стартовое значение счётчика
BПроверяет, продолжать ли цикл
CИзменяет счётчик после каждого повтора (делает шаг)
DРисует фигуру на холсте
3. Почему координату x фигуры внутри цикла записывают как 60 + i * 60, а не просто числом 200?
AЧтобы код был длиннее
BЧтобы каждая копия вставала на новое место в зависимости от номера i
CЧтобы цикл выполнился быстрее
DТак положено по синтаксису p5.js
4. Что произойдёт, если внутри цикла for забыть шаг i++?
AЦикл выполнится один раз и остановится
BЦикл вообще не запустится
CСчётчик не изменится, условие всегда истинно — получится бесконечный цикл и зависание
Dp5.js сам добавит шаг автоматически
5. Ты хочешь нарисовать клюв так, чтобы он переезжал вместе с каждым цыплёнком в ряду. Как правильно задать его координату?
AФиксированным числом, например triangle(82, ...)
BПривязать к базовой координате цыплёнка: например x + 22, где x зависит от i
CСлучайным числом random()
DКоординатой мыши mouseX
6. Чем отличается условие i <= 5 от i < 5 в цикле, начинающемся с i = 0?
AНичем, это одно и то же
Bi <= 5 даёт на один повтор больше (шесть вместо пяти)
Ci <= 5 запускает бесконечный цикл
Di <= 5 считает в обратную сторону