Урок 9, Цикъл "for"/"за"
Допълнителни линкове към този урок:
- Кода от този урок: примери за for цикли
- "Цикъл for! Нов вид цикъл", Khan Academy
- "Loops: while and for", javascript.info
- "JavaScript For Loop", W3Schools
- "Цикъл, управляван от брояч. Java", Уча.се (забележка: урока е за Java, не за JavaScript. Но общата идея е подобна.)
Основни точки от този урок
Миналия урок говорихме за цикъла while
, или "докато". Един друг важен цикъл е цикъла for
, който се използва много по-често от while
.
Думата "for" означава "за", в смисъла, в който може да кажем че нещо е "за някой" или пък "за всеки". Но тъй като е малко странно да наричаме този цикъл "за", когато дискутираме за този цикъл, обикновено използваме думата "фор" директно.
В най-общи линии, for
циклите са начин за по-лесно записване на while
цикли. Например, ако имаме един while
цикъл като този:
let x = 0 // начало
while (x < 400) { // условие
rect(x, 0, 50, 50) // Този код се изпълнява 4 пъти
= x + 100 // промяна
x }
То, може да го напишем ето така, използвайки for
:
for (let x = 0; x < 400; x = x + 100) { // начало, условие, промяна
rect(x, 0, 50, 50) // Този код се изпълнява 4 пъти
}
За да напишем един for
цикъл, първо имаме думичката for
и след нея две кръгли (()
) и две къдрави ({}
) скоби, както имахме при if
и while
.
В къдравите скоби (вторите), както обикновено, имаме кода, който се изпълнява при всяко повторение на цикъла. В случая, цикъла по-горе изпълнява rect(x, 0, 50, 50)
за да нарисува правоъгълник.
Обаче, в кръглите скоби, за разлика от while
, имаме три неща, отделени с точка и запетая (;
). Те са така наречените "инициализация" (или начало), "условие", и "актуализация" (или промяна).
Инициализацията или началото е това, което се изпълнява в началото на цикъла. В случая, това е let x = 0
и създава нова променлива с която си контролираме цикъла.
Условието е това с което решаваме дали да повторим още веднъж. В случая, това е x < 400
и може да забележете че е еднакво като условието на while
цикъла написан малко по-нагоре.
И последно, актуализацията или промяната е това което се изпълнява след всяко повторение на цикъла. Обикновено, тази част променя същата променлива която сме създали в началото. В случая, това е x = x + 100
, с което увеличаваме x
с 100.
![Диаграма на изпълнението на един for цикъл. Първо се изпълнява началната част на цикъла, след това се проверява условието. Ако условието е вярно, изпълняваме кода на цикъла, след което изпълняваме последната част / промяната, и преминаваме обратно към условието. Това се повтаря няколко пъти, докато условието спре да бъде вярно; тогава цикъла свършва и продължаваме да изпълняваме кода след цикъла.](/уроци/01-js/09_for_diagram.png)
Друг начин по който може да разберем един for
цикъл, е ако се опитаме да го преведем на нормален, човешки език; нещо което аз лично наричам "четене на код". В този случай, цикъла от по-горе, for (let x = 0; x < 400; x = x + 100)
, бих превел като "за всяко x
, започвайки от 0, до 400, увеличавайки със 100, прави ...", или пък като "за всяко x
от 0 до 400 през 100, прави ..."
Примери за for цикли
Но нека да разгледаме още няколко примера.
Досега, имахме следния for цикъл:
for (let x = 0; x < 400; x = x + 100) {
rect(x, 0, 50, 50)
}
Но нека да разгледаме и още няколко цикъла, които постигат същия резултат, но по малко по-различен начин:
0 ... N
В практиката, обикновено бих се опитал да напиша този цикъл по такъв начин, че промяната да увеличава променливата с 1. Това не е задължително, разбира се, но след като свикнете с записването на цикъла и с използването на математика, така е значително по-удобно.
В случая, ако променя x
да се увеличава с 1
, то ще трябва да го направя да се повтаря до 4
а отдолу ще умножа по 100
за да получа същите координати. Тъй като променливата вече не съдържа X координатата на правоъгълника а номера на правоъгълника, ще сменя името да не е x
ами i
(от "index" – номер):
for (let i = 0; i < 4; i = i + 1) { // 0; < N; +1
rect(i * 100, 0, 50, 50)
}
Прочетен, този цикъл ще го преведа като "Повтори 4 пъти, броейки от 0 (с променливата i
)"
Това е най-стандартния начин за записване на един for
цикъл. Когато започва от 0 и условието е да е по-малко от някакво число, то това число е колко на брой пъти нашия цикъл се повтаря.
1 ... N
Друг стандартен начин да запишем един for
цикъл е ако го направим да започва да брои от 1. За целта, ще сменим началната стойност да е 1, а условието ще напишем с <=
– така, броя повторения е лесно видим за който чете кода:
for (let i = 1; i <= 4; i = i + 1) { // 1; <= N; +1
rect(i * 100 - 100, 0, 50, 50)
}
Прочетен, този цикъл ще го преведа като "Повтори 4 пъти, броейки от 1 (с променливата i
)"
Забележете, че в случая ми се наложи да извадя 100 когато смятам координатите. Тъй като подобно изваждане се налага често когато започваме от едно, повечето програмисти предпочитат да започват от нула
N ... 0
Последно, нека да видим как може да направим цикъл който да брои на обратно.
Първо промяната ще трябва да направим да намалява, вместо да увеличава, променливата с 1.
Но също така, ще трябва да сменим знака на условието да използва по-голямо >
вместо по-малко (<
) – иначе, ще получим безкраен цикъл. ⚠️
Крайния резултат изглежда нещо подобно на това:
for (let i = 4 - 1; i >= 0; i = i - 1) { // N - 1; >= 0; -1
rect(i * 100, 0, 50, 50)
}
В случая, искам да броя от нула. Тоест, ако имам четири правоъгълника, то първия има номер 3, после 2, 1, и накрая 0. Затова и започвам цикъла от 4 - 1
и завършвам с >= 0
. По този начин получавам същите числа като в "стандартния" for
цикъл който брои от 0.
Този цикъл рисува същите правоъгълници като цикъла започващ от 0 по-горе, но ги рисува в обратен ред. Ако направите правоъгълниците по-широки, ще видите разликата.
И.. още малко съкращения
Последно, може би сте забелязали как името на променливата се повтаря когато искаме да я увеличим или намалим. Например, при i = i - 1
, имаме "i" написано два пъти. Тъй като по някой път тези имена на променливи са доста дълги, програмистите от доста време използват и съкращения за увеличаването или намаляването на променлива.
В следната таблица може да видите някои от тези съкращения:
Код без съкращение | Съкратен код | Съкращение | Значение на съкращението |
---|---|---|---|
x = x + 50 |
x += 50 |
+= |
Увеличи променливата с ... |
x = x - 50 |
x -= 50 |
-= |
Намали променливата с ... |
x = x + 1 |
x ++ |
++ |
Увеличи променливата с 1 |
x = x - 1 |
x -- |
-- |
Намали променливата с 1 |
(подобни съкращения с аналогично значение съществуват като *=
, /=
, а дори и &&=
или ||=
)
Използвайки тези съкращения, горните цикли може да напишем и така:
for (let x = 0; x < 400; x += 100) { // x = 0, 100, 200, 300
rect(x, 0, 50, 50)
}for (let i = 0; i < 4; i ++) { // i = 0, 1, 2, 3
rect(i * 100, 0, 50, 50)
}for (let i = 1; i <= 4; i ++) { // i = 1, 2, 3, 4
rect(i * 100 - 100, 0, 50, 50)
}for (let i = 4 - 1; i >= 0; i ++) { // i = 3, 2, 1, 0
rect(i * 100, 0, 50, 50)
}
Задачa
![%Концентрични кръгове](/уроци/01-js/09_concentric.png)
![%Ефект "Моар"](/уроци/01-js/09_moire.png)
Използвайки for
цикъл, нарисувайте поредица от концентрични кръгове (т.е. кръгове които имат един и същ център.) Трябва да получите нещо като първата картинка.
(Използвайте noFill()
за да получите само очертанията на кръгчетата, без запълване.)
След това, повторете същия (или подобен) цикъл за да направите още една група концентрични кръгове малко по-встрани.. така че да се пресичат, както на втората картинка.
Виждате ли "линиите" които сякаш се появяват там къде се пресичат кръговете и продължават навън? Това е така наречения ефект "Моар" / "Moiré" – и е често срещано явление когато имате много линии (независимо дали са прави или криви), които са една до друга и се пресичат. Например, може да видите същия ефект ако погледнете към тънко перде зад което има мрежа за прозорци.
Опитайте да промените разликата в размерите на кръговете или пък да промените разстоянието между двете групи кръгове. Как се променя ефекта? Можете ли да направите едната група да е анимирана по някакъв начин, или пък да се движи с мишката?
Упражнения
Следните упражнения целят да помогнат за разбирането на for циклите. Вижте дали може да отговорите правилно – но ако да объркате, няма проблем; доста от упражненията са леко променени, или дори подвеждащи, като целта е да ви помогнат да намирате подобни несъответствия, когато вие самите пишете код.
(Забележка: /* ... */
е алтернативен начин за писане на коментари в кода, използван тук единствено за да може упражненията да заемат по-малко редове. Коментара не променя начина, по който работи цикъла.)
Колко пъти биха се повторили следните цикли?
for (let i = 0; i < 15; i ++) { // ... }
Отговор:
15 пъти (0, 1, 2, ... 13, 14). Подобен на примерния цикъл разглеждан в урока.for (let i = 0; i < 40; i ++) { /* ... */ }
Отговор:
40 пъти (0, 1, 2, ... 38, 39). Подобен на примерния цикъл разглеждан в урока.for (let i = 0; i < 15; i += 3) { /* ... */ }
Отговор:
5 пъти (0, 3, 6, 9, 12). Като горните, но вече се увеличава с 3 – следователно, получаваме 3 пъти по-малко повторения (а останалите ги прескачаме).for (let i = 1; i <= 23; i ++) { /* ... */ }
Отговор:
23 пъти (1, 2, 3, ... 12, 22, 23). Подобен на примерния цикъл разглеждан в урока.for (let i = 0; i <= 23; i ++) { /* ... */ }
Отговор:
24 пъти (0, 1, 2, ... 21, 22, 23). За разлика от горния започва от 0, но все още е с
<=
и така брои още една нула (/ брои до 23 включително).for (let i = 1; i < 23; i ++) { /* ... */ }
Отговор:
22 пъти (1, 2, ... 21, 22). За разлика от по-горния е с
<
, но все още започва от 1 и така не брои 23.for (let i = 1; i <= 100; i = i * 2) { /* ... */ }
Отговор:
Стойностите на
i
са 1, 2, 4, 8, 16, 32, 64, 128.. но 128 вече е извън цикъла. Изброявайки, получаваме 7 пъти. Малко по-нестандартна задача, но този тип цикли се срещат по някога.for (let i = 15 - 1; i >= 0; i --) { /* ... */ }
Отговор:
15 пъти (14, 13, ... 2, 1, 0). Подобен на примерния цикъл разглеждан в урока.for (let i = 6; i >= 0; i --) { /* ... */ }
Отговор:
7 пъти (6, 5, 4, 3, 2, 1, 0). Почти като горния, но без `-1`.for (let i = 15 - 1; i < 0; i --) { /* ... */ }
Отговор:
0 пъти; но ако започваше от отрицателно число, цикъла би бил безкраен. Почти като по-горния, но с `<` вместо `>=`.
Попълни празното
Какво може да напишем на мястото на
___
така че цикъла да се повтаря 16 пъти?for (let i = 0; ___; i ++) { /* ... */ }
Отговор:
i < 16
илиi <= 15
(0, 1, ..., 14, 15). Подобен на примерния цикъл разглеждан в урока.Какво може да напишем на мястото на
___
така че цикъла отново да се повтаря 16 пъти?for (let i = 3; ___; i ++) { /* ... */ }
Отговор:
i < 19
илиi < 3 + 16
илиi <= 18
(3, 4, ..., 17, 18). Тъй като започваме с 3 а не с 0, трябва да "преместим" и другия край с 3.Какво може да напишем на мястото на
___
така че цикъла да се повтаря 5 пъти?for (let i = 0; i < 40; ___) { /* ... */ }
Отговор:
i += 8
илиi += 40 / 5
илиi = i + 8
(илиi += 9
или дориi *= 2.6
). Подобен на най-първия цикъл, който разглеждахме в урока. Нужно е да увеличаваме с повече от 1, иначе цикъла би се повторил твърде много пъти (примерно 40).Какво може да напишем на мястото на
___
така че цикъла да се повтаря 4 пъти?for (___; i <= 4; i ++) { /* ... */ }
Отговор:
let i = 1
(1, 2, 3, 4). Подобен на примерния цикъл разглеждан в урока.Какво може да напишем на мястото на
___
така че цикъла да се повтаря 10 пъти?for (___; i < 10; i ++) { /* ... */ }
Отговор:
let i = 0
(0, 1, ..., 8, 9). Подобен на примерния цикъл разглеждан в урока.Какво може да напишем на мястото на
___
така че цикъла да се повтаря 8 пъти?for (___; i >= 0; i --) { /* ... */ }
Отговор:
let i = 8 - 1
илиlet i = 7
(7, 6, ... 1, 0). Подобен на примерния цикъл разглеждан в урока.Какво може да напишем на мястото на
___
така че цикъла да се повтаря 12 пъти?for (___; i > 0; i --) { /* ... */ }
Отговор:
let i = 12
(12, 11, ... 2, 1). За разлика от горния цикъл, този има> 0
, и затова ще свърши с1
.
Приложение
Как бихме могли да напишем
for
цикъл, който брои (поредни числа) от 0 и се повтаря 7 пъти? (0, 1, ..., общо 7 числа)Отговор:
for (let i = 0; i < 7; i ++) { /* ... */ }
Променливата може да е с друго име.
i = i + 1
може да не е съкратено.i <= 6
би имало същия ефект (но ще е по-нечетливо).Как бихме могли да напишем
for
цикъл, който брои (поредни числа) от 1 и се повтаря 13 пъти? (1, 2, ..., общо 13 числа)Отговор:
for (let i = 1; i <= 13; i ++) { /* ... */ }
Променливата може да е с друго име.
i = i + 1
може да не е съкратено.i < 14
би имало същия ефект (но ще е по-нечетливо).Как бихме могли да напишем
for
цикъл, който брои от 20 до 0 включително? (20, 19, ..., 1, 0)Отговор:
for (let i = 20; i >= 0; i --) { /* ... */ }
Променливата може да е с друго име.
i = i - 1
може да не е съкратено. Може да се напише и сi = 21 - 1
.i > -1
би имало същия ефект.
Задачата е леко подвеждаща, тъй като определя началното число от което да започнем (20), вместо да определи колко пъти трябва да се повтори (21). Ако сте я разбрали иначе, и сте направили цикъл чието първо число е 19, няма проблем – просто ще трябва да го увеличите с 1 за да съвпадне с условието! 😁
Както казах най-отгоре, няма нужда да сте решили упражненията на 100% – те са по-скоро част от урока, а не част от някакъв "изпит". Дори да не сте познали нито едно упражнение, надявам се, че те са ви били от полза за това да разберете как работи един for
цикъл! 😃
Ако искате да експериментирате с for
цикли в допълнение на тези упражнения, бихте могли да използвате код подобен на този:
let c = 0 // брояч, следи колко пъти сме изпълнили кода
for (let i = 0; i < 4; i ++) { // примерен цикъл, може да го променяте, както желаете
++ // увеличаваме брояча с 1
c
}
text(c, 200, 200) // изписваме брояча в средата на екрана/платното -- с примерния цикъл, ще получим 4, тъй като той се изпълнява 4 пъти
// (има и други начини да изпишем брояча, просто този е начина, който сме учили до тук)
По този начин, ще получите броя повторения на екрана!