Slovník pojmů
Cyklus v programování
Cyklus opakuje kód pro kolekci nebo do splnění podmínky. Musí být jasné, co tvoří jednu iteraci, kdy skončí a zda se během ní nemění data, která právě prochází.
Stručná definice
Opakování s viditelným ukončením.
Iterace je jedno provedení těla cyklu. Zdroj prvků bývá pole, rozsah čísel nebo stav řízený podmínkou. Cyklus není rekurze: rekurze znovu volá funkci a potřebuje základní případ, zatímco cyklus opakuje blok v jedné konstrukci.
Počet iterací není automaticky index pole. U souvislého seznamu jsou indexy běžně od nuly, ale například PHP array může mít nesouvislé klíče. Proto je foreach často bezpečnější vyjádření práce s položkami než ruční počítadlo.
Jaký problém řeší
Zpracuje každou položku bez opakování stejného zápisu.
Cyklus se používá pro součet objednávky, vykreslení seznamu, práci s dávkou nebo procházení validovaných dat.
- součet cen položek objednávky
- zpracování výsledků API nebo importu
- vykreslení kolekce ve frontendovém rozhraní
- opakování akce, dokud nenastane definovaný konec
Praktický příklad
Součet položek objednávky pomocí foreach
foreach vyjadřuje, že chceme pracovat s každou položkou kolekce, ne s indexem. Částky jsou v haléřích, takže ukázka neřeší chybu plovoucí čárky. Pracuje již s validovanými položkami přesně popsaného tvaru.
Pokud položky leží v databázi, není vždy vhodné provést pro každou další dotaz. U většího objemu zvažte přednačtení nebo hromadnou databázovou operaci podle business pravidel.
PHP
/** @param list<array{quantity: positive-int, unitPriceInCents: int}> $items */
function calculateItemsTotal(array $items): int
{
$totalInCents = 0;
foreach ($items as $item) {
$totalInCents += $item['quantity'] * $item['unitPriceInCents'];
}
return $totalInCents;
}
Jak funguje
Průchod položkami objednávky
Přístupný diagram ukazuje nejběžnější případ kolekce.
- Začátek Součet má počáteční hodnotu nula.
- Další položka Cyklus vezme jednu hodnotu z kolekce.
- Tělo cyklu Připočítá řádkovou cenu do součtu.
- Ukončení Po poslední položce vrátí výsledek.
Hlavní části a varianty
for, foreach a while nevyjadřují totéž.
Volba konstrukce má popsat, co řídí opakování.
for
Hodí se pro známý rozsah nebo počítadlo, když má index skutečný význam.
foreach
Prochází prvky kolekce a obvykle nezavádí chybu v ručním indexu.
while a do while
while ověřuje podmínku před tělem, do while jej provede alespoň jednou.
break a continue
break cyklus ukončí, continue přeskočí jen aktuální iteraci; jejich záměna mění výsledek.
Omezení a časté chyby
Opakování může růst rychleji, než je vidět.
Co pomáhá
- vybrat foreach pro kolekci a for jen při významném indexu
- zviditelnit podmínku ukončení
- zvlášť testovat prázdnou i hraniční kolekci
Rizika
- while bez ověřitelného ukončení
- i <= count($items) jako off-by-one u souvislého seznamu
- změna kolekce během iterace
- vnořené cykly nebo databázový dotaz v každé iteraci bez měření
Praktické použití
Pohodlný zápis nesmí skrýt cenu provedení.
Vnořené cykly mohou násobit práci s daty. To není automaticky chyba, ale u větších kolekcí je potřeba znát objem, provést měření a případně změnit algoritmus nebo práci s databází.
Nekonečný cyklus může být záměrný například v event loopu nebo workeru, musí však mít řízené blokování, obsluhu selhání a způsob ukončení procesu.
Změna kolekce během iterace může podle jazyka a použité konstrukce přeskočit prvek nebo vytvořit nečekaný stav. Je proto vhodné nejdřív určit, zda se má pracovat se snímkem dat, vytvořit nová kolekce, nebo změnu odložit až po průchodu. U ručního indexu je nutné rozlišit počet prvků od posledního platného indexu; u souvislého seznamu je poslední index count($items) − 1.
Cyklus bývá jen jedním krokem algoritmu. Je-li v každé iteraci databázový dotaz, může vzniknout N+1 problém; někdy pomůže přednačtení dat nebo hromadná operace. Správná volba ale závisí na pravidlech, objemu i čitelnosti, proto ji nelze nahradit slepým zákazem všech vnořených cyklů.
break má ukončit pouze cestu, kdy už další položky nemohou změnit výsledek. continue je vhodné jen tehdy, když je srozumitelné, proč se jedna položka přeskočí. Pokud se takové rozhodování opakuje, může být čitelnější data nejdřív filtrovat nebo pravidlo vyjmout do pojmenované funkce.
U zpracování velké dávky má každá iterace také provozní dopad. Je vhodné rozhodnout, zda se chyba jedné položky zapíše a dávka pokračuje, nebo se práce ukončí a může bezpečně navázat od posledního potvrzeného bodu. Takové pravidlo není vlastností for ani foreach, ale způsob určení ukončení a práce s chybou rozhoduje o spolehlivosti celého procesu.
Kontrola změny
Každé opakování má mít důvod a konec.
Cyklus je bezpečnější, když z kódu vyčtete zdroj prvků, stav změny a ukončení.
- ověřit prázdnou kolekci a poslední iteraci
- neplést počet iterací s indexem nebo klíčem
- používat break jen pro skutečné ukončení
- vyhnout se N+1 dotazům v opakování
Časté otázky
Cyklus v praxi
Kdy použít for a kdy foreach?
foreach pro prvky kolekce, for pokud potřebujete rozsah nebo index jako součást pravidla.
Jaký je rozdíl mezi break a continue?
break ukončí celý aktuální cyklus, continue přeskočí jen zbytek jedné iterace.
Je každý nekonečný cyklus chyba?
Ne, ale musí být záměrný a provozně řízený.
Je cyklus totéž co rekurze?
Ne. Rekurze opakuje volání funkce, cyklus opakuje blok kódu.
Osobní zkušenost
Opakované zpracování musí zůstat přehledné i nad většími daty.
V PHP aplikacích volím cykly podle tvaru dat a sleduji jejich dopad na databázové operace i výkon.