Cykly Intermezzo Rozhodl jsem se zaadit do série nkolika lánk o základech programování v Delphi/Pascalu malou vsuvku, která nám pomže pochopit principy a zásady pi používání tzv. cykl. Mnoho ástí i jednoduchých algoritm používá práv této opakující se struktury. Pokud zstaneme v teoretické rovin, mžeme hovoit o dvou základních druzích cykl, které mžeme využít. Pokud budeme obecn hovoit o cyklických strukturách, mohli bychom najít i celou adu dalších struktur, které využívají možnosti opakování. (Tady bych zaadil použití tzv. návstí - LABEL, nebo také využití tzv. rekurzivního volání procedury nebo funkce. Tato témata ovšem rozhodn nepatí do základního kurzu programování.) Cykly, o kterých si budeme povídat, najdeme v defininí struktue Delphi celkem ti: While - do Repeat - until For do Obecn lze tyto cykly také rozdlit podle toho, zda pi použití pedem známe poet opakování, totiž že ho také sami volíme (cyklus FOR), nebo pedem poet opakování neznáme a závisí na urité podmínce (WHILE a REPEAT). Cyklus se tedy opakuje tak dlouho, dokud platí uritá podmínka zadaná ze strany programátora. Oba zpsoby nalézají uplatnní v programování, lze ovšem íct, že první skupinu vždy mohu nahradit druhou a ne naopak, jinými slovy, vždy mohu cyklus FOR nahradit cyklem napíklad WHILE, obrácen to ovšem nejde vždycky. FOR cyklus Tento cyklus je asi nejjednodušší a použijeme ho všude tam, kde máme jasn daný poet opakování. Typickým píkladem pro použití tohoto cyklu je napíklad práce s tzv. polem, (S tímto datovým typem se seznámíme v další lekci.) což by se do praxe dalo pevést jako práce s njakou maticí nebo vektorem. Abychom se v tuto chvíli mohli s použitím cyklu seznámit, volím úpln jiný píklad a zkusím vypsat na obrazovku posloupnost ísel 1 10. Bez použití cyklu bych mohl napsat takovouto ást programu: writeln(1); writeln(2); writeln(3); writeln(4); writeln(5); writeln(6); writeln(7); writeln(8); writeln(9); writeln(10); Kdybych dostal za úkol vypsat adu ísel 1 1000, bylo by to pro m urit dost nepohodlné Proto zvolím ješt jiný zpsob a použiji cyklus. V tomto pípad pedkládám celý program, aby bylo patrné, že jsem pro tuto potebu musel ješt navíc deklarovat promnou i: 1
program CyklusApp; {$APPTYPE CONSOLE} uses SysUtils; Var i: integer; BEGIN for i:= 1 to 10 do END. Cyklus FOR piazuje po jednice do promnné i postupn hodnoty 1-10. Za píkazem DO následuje píkaz, který se má vykonat. Jestliže by bylo zapotebí provést v cyklu více píkaz, uzavou se do bloku end: for i:= 1 to 10 do write('vypisuji cislo: '); Obr. 0-1 Výsledek použití cyklu FOR. Abych tuto strukturu popsal kompletn, je teba piznat, že cyklus nemusí opakovat provedení uritých píkaz programu vždy od menšího ísla k vtšímu, ale také naopak, napíklad od 10 do 1, což by se dalo v programu zapsat napíklad takto: for i:= 10 downto 1 do write('vypisuji cislo: '); 2
Obr. 0-2 Obrácený cyklus FOR WHILE cyklus Všude tam, kde pedem není jasný poet opakování, mžeme použít tento cyklus. Za píkazem WHILE vždy následuje podmínka, která uruje, kolikrát nebo jak dlouho se bude cyklus opakovat, pestože pedem nejsme schopni íci, kolik takových opakování bude. V tomto smru mžeme snadno sklouznout do ošemetné situace, kdy se program tzv. zacyklí, což znamená, že nikdy nebude dosažena podmínka, která by cyklus ukonila, jinak bych mohl íct, že podmínka, za které se provádjí píkazy v cyklu, bude platit vždy a nikdy se nezmní. Je tedy dležité podmínce vnovat pozornost obzvlášt v pípad, že s programováním zaínáme. Následovat by ml píkaz DO a za ním jeden, nebo skupina píkaz, které se v cyklu mají provádt. Hned na úvod si mžeme ukázat, když jsem o tom už v pedcházejících odstavcích mluvil, jakým zpsobem tedy nahradíme FOR cyklus cyklem WHILE jen proto, abychom si ukázali správnou syntaxi, tedy zápis, v programovém jazyce: i:= 1; while i<=10 do write('vypis pomoci WHILE cyklu, cislo: '); 3
Obr. 0-3 Výpis ady ísel pomocí WHILE cyklu Oproti cyklu FOR musím sám zvyšovat hodnotu promnné i, takže za výpisem ísla následuje ješt ádek s píkazem, se kterým se jako programátor setkáte asto, kdy do promnné piazuji její stávající hodnotu a navíc zvyšuji o jinou hodnotu, v tomto pípad o jedniku. Což bych mohl také nahradit procedurou inc(i): Dokud bude splnna podmínka cyklu, že hodnota promnné i bude menší nebo rovna 10, bude se provádt blok píkaz v cyklu. Navíc se v cyklu hodnota promnné zvyšuje, což je pro tento píklad dost podstatné. Kdybych tento píkaz vynechal, hodnota promnné by se nemnila a podmínka by platila vždy a nikdy by nedošlo k ukonení cyklu! Program by tzv. zamrzl. Tento píklad píliš neprezentuje výhodu tohoto cyklu. Zatím jsme si jen ukázali, jak bychom jej mohli použít z hlediska zápisu. Podívejme se nyní na jiný píklad, kdy budeme chtít vždy sítat dvojici stejných ísel a to tak dlouho, než bude souet vtší než 10. eknme, že použijeme promnnou i, kterou budeme od 1 zvyšovat, dokud souet i+i nebo souin 2 * i nebude vtší než deset. Samozejm si mžeme pedem spoítat, kolik opakování program provede, ale berme úlohu také z pohledu uživatele, který by si mohl limitní výsledek soutu (u nás 10) libovoln zvolit. Tuto úlohu bychom mohli zapsat teba takto: program CyklusApp; {$APPTYPE CONSOLE} uses SysUtils; Var i: integer; Function MySUM(x: integer ): integer; MySUM:= x+x; 4
BEGIN i:= 1; while MySUM(i) <= 10 do write('cislo: '); write('soucet je: '); writeln(mysum(i)); END. Pestože uplatnní tohoto píkladu je tém nulové, názornost použití jednak cyklu, jednak funkce je zejmá: Použil jsem funkci, která k uvedenému íslu pite totéž íslo, tak jak bylo požadováno v zadání. Funkci jsem použil v podmínce, která udává, za jakých okolností se blok cyklu má provádt, totiž do té doby, dokud bude výsledný souet menší nebo práv roven deseti. Pro názornost v bloku cyklu vypisuji, jaké íslo je vstupním parametrem funkce a jaký je výsledek soutu. Samozejm nesmím zapomenout promnnou i vždy v bloku cyklu zvýšit, abych zajistil konené ešení, tedy výsledek, který již podmínce nebude vyhovovat: Obr. 0-4 Výsledek použití WHILE cyklu Když si pedstavíme hodnotu i = 6, potom je zejmé, že dvojnásobek této hodnoty už bude vtší než deset, cyklus se dále provádt nebude a program bude pokraovat dál na ádku: REPEAT cyklus Podobn jako v pedchozím pípad mžeme tento cyklus použít i všude tam, kde pedem neznáme poet opakování, ale víme, že díky urité podmínce má algoritmus konené ešení, tedy poet cykl, které se provedou, je konený. Na rozdíl od cyklu WHILE, kdy jsme psali 5
podmínku hned na zaátku, tedy ješt pedtím, než jsme programu definovali blok píkaz, které se budou cyklicky opakovat, je tomu v tomto pípad naopak: Cyklus nejprve provede první ást bloku, než dojde k vyhodnocení podmínky. Tato vlastnost se v programování mže hodit a je to také zpsob, pro máme obecn možnost rozhodnout mezi WHILE a REPEAT cyklem. Abych rozdíl ješt více ozejmil, navrhnu podmínku v tomto cyklu tak, abych dosáhl stejného výsledku. Porovnáním obou podmínek je také zejmé využití jednoho nebo druhého cyklu a také možnost jejich zámny. (Alespo v nkterých pípadech ) program CyklusApp; {$APPTYPE CONSOLE} uses SysUtils; Var i: integer; Function MySUM(x: integer ): integer; MySUM:= x+x; BEGIN i:= 1; repeat write('cislo: '); write('soucet je: '); writeln(mysum(i)); until MySUM(i) > 10; END. Opt ponechávám definovanou funkci, kterou využívám jednak ve výpisu, jednak v podmínce. Ta musí být ovšem zapsána jako negace pedešlé podmínky, aby výsledek byl stejný. Cyklus se opakuje až do (until) podmínky, kdy výsledek už je vtší než deset. Pak se cyklus ukoní. Závrem bych ekl jen jedno: Záleží na okolnostech, pro který z popisovaných cykl se rozhodnete. Obecn platí, že cyklus FOR je zastupitelný obma z dále uvedených a navíc i tyto se mezi sebou dají zamnit. Existují ovšem pípady, kdy je vhodné rozhodnout se konkrétn pro ten i onen cyklus. Ale na to už musíte pijít sami. Chce to jenom programovat, programovat a zase programovat 6