Níže uvedený text pochází z prvního vydání. Nad tímto textem se nachází aktuální stav po revizi směřující k druhému vydání.
O čem si budeme povídat?
Řekneme si, jak užívat cykly, abychom se vyhnuli opakovanému psaní příkazů. Ukážeme si různé typy cyklů a to, kdy je máme použít.
Na konci předchozí kapitoly jsme si vytiskli část tabulky násobků čísla 12. Ale stálo nás to mnoho psaní a pokud bychom potřebovali tabulku rozšířit, bylo by to velmi časově náročné. Naštěstí existuje lepší způsob a na něm si ukážeme opravdovou sílu, kterou nám programovací jazyky nabízejí.
Nyní si ukážeme, jak v programovacím jazyku zapíšeme, že se něco má opakovat. Budeme dosazovat hodnotu proměnné a při každém opakování ji současně budeme zvyšovat. Zápis v jazyce Python vypadá takto:
>>> for i in range(1, 13): ... print "%d x 12 = %d" % (i, i*12) ...
Poznámka 1: V příkazu range(1, 13) musíme použít 13,
protože range() generuje čísla od dolní hranice včetně až po
horní zadanou hranici vyjma. Na první pohled se vám to může zdát podivné,
ale jsou pro to dobré důvody a časem si na to zvyknete.
Poznámka 2: Operátor for jazyka Python je ve
skutečnosti operátorem, který bývá označován jako foreach (doslova pro
každý). Následující posloupnost příkazů je provedena pro každý
prvek kolekce. V našem případě je touto kolekcí seznam čísel generovaných
funkcí range(). Můžete si to ověřit zapsáním příkazu
print range(1, 13) na příkazový řádek interpretu
jazyka Python. Uvidíte, co se vytiskne.
Poznámka 3: Řádek s příkazem print je odsazen více,
než předcházející řádek s příkazem for. To je velmi důležité,
protože tím překladači jazyka Python dáváme najevo, že chceme opakovat právě
příkaz print. Nezáleží na tom, jak velké odsazení použijete,
ale zvolenou hodnotu odsazení musíte dodržovat.
Poznámka 4: Pokud s překladačem jazyka Python pracujete v interaktivním režimu, spustí se program až po dvojím stisku klávesy Enter. Důvod spočívá v tom, že po prvním stisku překladač jazyka Python nepozná, zda chcete k posloupnosti opakovaných příkazů přidat další řádek, či nikoliv. Pokud stisknete klávesu Enter podruhé, Python předpokládá, že jste již dokončili vkládání příkazů a program spustí.
Takže jak vlastně uvedený program pracuje? Projdeme si jej krok po kroku.
Python nejdříve použije funkci range() pro vytvoření seznamu
čísel od 1 do 12. Poté Python přiřadí proměnné i první hodnotu
seznamu. Následuje provedení odsazeného kódu při použití hodnoty
i = 1:
print "%d x 12 = %d" % (1, 1*12)
Potom se Python vrátí zpět na řádek příkazem for a přiřadí
i další hodnotu se zeznamu, tentokrát 2. Opět se provede
odsazený kus kódu, tentokrát s hodnotou i = 2:
print "%d x 12 = %d" % (2, 2*12)
Python bude odsazenou posloupnost příkazů opakovat až do doby, kdy byly
proměnné i přiřazeny všechny hodnoty seznamu. V ten
okamžik, po provedení
těla cyklu s poslední hodnotou v proměnné i, se
provádění přesune na další příkaz, který není odsazen. V našem
příkladu žádné další příkazy nemáme, takže dojde k ukončení programu.
FOR I = 1 to 12
PRINT I, " x 12 = ", I*12
NEXT I
V tomto zápisu je mnohem lépe na první pohled vidět, co se děje. Nicméně verze v jazyce Python je mnohem pružnější, protože cyklus může probíhat pro řadu čísel, pro položky seznamu nebo pro jakýkoliv jiný typ kolekce (například pro řetězec).
Poznámka překladatele: Zápis příkladu v jazyce Python můžeme přiblížit podobě příkladu v jazyce BASIC tím, že nevyužijeme vlastností formátovacího řetězce:
>>> for i in range(1, 13): ... print i, "x 12 =", i*12 ...
Jednou z výhod použití formátovacího řetězce je ale například možnost předepsat, jak se má číslo naformátovat — tj. na kolik pozic se má tisknout. Vyzkoušejte:
>>> for i in range(1, 13): ... print "%2d x 12 = %3d" % (i, i*12) ...
Konstrukce for, která je běžná v mnoha pogramovacích
jazycích, má v jazyce Tcl podobu známou z jazyka C. Vypadá následovně:
for {set i 1} {$i <= 12} {incr i} {
puts [format "%d x 12 = %d" $i [expr $i*12]]
}
Poznámka: Tato konstrukce má 3 části:
{set i 1} se provede
pouze jednou, před vším ostatním,{$i <= 12}, která se vykonává
před každou iterací, tj.
před každou obrátkou cyklu a{incr i}, která se vykonává po
každé iteraci.Tělo cyklu se provede pouze v případě, kdy je podmínka v testové části splněna. Každá z uvedených částí může obsahovat libovolný kód, ale výsledkem výrazu v testové části musí být boolovská hodnota (což v případě jazyka Tcl znamená nulovou nebo nenulovou hodnotu). Ačkoliv jsem tělo cyklu zapsal odsazené, učinil jsem tak pouze pro zvýšení srozumitelnosti zápisu. Tcl nevyžaduje odsazování bloků. Místo toho se k označování začátku a konce bloku používají složené závorky.
V jazyce Tcl existuje také konstrukce foreach, kterou lze
použít pro seznamy.
Cykly typu FOR nepředstavují jediný možný typ konstrukce
cyklu. A to je dobře, protože u cyklu FOR musíme vědět, nebo
musíme být schopni předem vypočítat, počet prováděných iterací. Takže co
máme dělat v případech, kdy chceme pokračovat v provádění určitého úkolu až
do doby, kdy nastane určitá situace, ale když přitom nevíme, kdy k dané
situaci dojde? Můžeme například chtít načítat a zpracovávat data ze souboru,
ale předem nevíme, kolik datových položek soubor obsahuje. Chtěli bychom
prostě pokračovat ve zpracování dat až do dosažení konce souboru. Lze k tomu
sice použít i cyklus FOR, ale je to obtížné.
K řešení tohoto problému se hodí jiný typ cyklu — cyklus typu WHILE. V jazyce BASIC by zápis vypadal nějak takto:
J = 1
WHILE J <= 12 DO
PRINT J, " x 12 = ", J*12
J = J + 1
WEND
Tento kód produkuje stejný výstup, jako v předchozím případě, ale místo
cyklu for jsme použili cyklus while. Povšimněte
si, že za while zapisujeme výraz, jehož výsledkem je
boolovská hodnota (true nebo false — pamatujete?). Pokud je výsledkem
hodnota true (pravda), pak bude kód uvnitř cyklu proveden.
Jako alternativu si nyní ukážeme verzi v Tcl:
set j 1
while {j <= 12} {
puts [format "%d x 12 = %s" $j [expr $j*12]]
set j [expr $j + 1]
}
Jak vidíte, struktura příkazu while je docela podobná. Pouze místo
WEND v jazyce BASIC se zde používají složené závorky. Ale co to
je za zmatek v těle cyklu? Pamatujete si ještě na použití formátovacího
řetězce v jazyce Python? Zápis format je jeho jeho ekvivalentem
v jazyce Tcl. Zápis $j vyjadřuje hodnotu proměnné
j (takto se odliší od zápisu písmene 'j') a expr
prostě říká vyhodnoť následující zápis jako výraz. Hranaté závorky
Tcl říkají, které části se mají zpracovávat nejdřív. Tcl je poněkud
neobvyklý jazyk v tom, že provádí interpretaci svého kódu v jednom průchodu.
Takže bez závorek by se pokoušel vytisknout slovo 'expr', potom by
zpozoroval další hodnoty a po výpisu chybového hlášení by to vzdal.
Potřebujeme mu říct, že má nejdříve provést součet, potom naformátovat
řetězec a vytisknout výsledek. Jste zmateni? Nedělejte si s tím starosti.
Jak už jsem řekl, Tcl je poněkud neobvyklý jazyk s několika výjimečně
dobrými možnostmi a s velkým množsvím podivností.
A nyní se podívejme na Python:
>>> j = 1 >>> while j <= 12: ... print "%d x 12 = %d" % (j, j*12) ... j = j + 1
V tomto okamžiku už by se vám to mohlo zdát docela jasné. Chtěl bych vás
jen upozornit na jednu věc. Vidíte tu dvojtečku na konci řádku s příkazem
while a před tím na konci řádku s for? Právě ta
překladači jazyka Python říká, že bude následovat úsek kódu (blok).
Většina jazyků používá pro označení konce bloku nějakou značku (například
BASIC používá WEND, Tcl složené závorky), ale Python pro
vyjádření struktury používá odsazení. To znamená, že je důležité dodržet
stejné odsazení všech řádků uvnitř cyklu. Ono to stejně patří k dobrým
zásadám, které zvyšují čitelnost kódu.
Vraťme se zpět k naší tabulce násobení číslem 12 ze začátku této kapitoly. Cyklus, který jsme vytvořili, se pro tisk takové tabulky velmi dobře hodí. Ale jak by to bylo s jinými hodnotami? Mohli bychom cyklus upravit tak, aby produkoval tabulku násobků třeba číslem 7? Mělo by to vypadat nějak takto:
>>> for j in range(1, 13): ... print "%d x 7 = %d" % (j, j*7)
Při úpravě jsme museli hodnotu 12 změnit na hodnotu 7 a to na dvou místech. A pokud bychom chtěli použít jinou hodnotu, museli bychom ji, opět na dvou místech, změnit znovu. Nebylo by lepší, kdybychom mohli nějakým obecnějším způsobem zadat požadovaného násobitele?
Můžeme toho dosáhnout tím, že místo konkrétní hodnoty použijeme další proměnnou. Hodnotu této proměnné nastavíme před zahájením cyklu:
>>> nasobitel = 12 >>> for j in range(1,13): ... print "%d x %d = %d" % (j, nasobitel, j*nasobitel)
Takto získáme naši starou známou tabulku násobení číslem 12. Ale pokud
nyní budeme chtít násobit sedmi, stačí, když změníme pouze hodnotu proměnné
nasobitel.
Povšimněte si, že zde kombinujeme zápis posloupnosti příkazů a příkaz
cyklu. Nejdříve jsme použili jednoduchý příkaz
nasobitel = 12, za kterým následuje v pořadí
další příkaz cyklu for.
Použijme nyní předchozí příklad k dalšímu kroku. Dejme tomu, že chceme
vytisknout všechny tabulky násobků čísel od 2 do 12 (násobení číslem 1 je
příliš jednoduché než abychom se jím zabývali). Jediné, co musíme učinit, je
použít proměnnou nasobitel jako součást dalšího cyklu:
>>> for nasobitel in range(2, 13): ... for j in range(1, 13): ... print "%d x %d = %d" % (j, nasobitel, j*nasobitel)
Všimněte si, že odsazená část uvnitř prvního cyklu for je
zápisem přesně téhož cyklu, s kterým jsme začínali. Funguje to následovně:
Nastavíme nasobitel na první hodnotu (2) a provedeme druhý
cyklus. Poté hodnotu proměnné nasobitel změníme na následující
hodnotu (3) a znovu provedeme vnitřní cyklus, a tak dále. Tato technika je
známa jako vnořování cyklů.
Drobnou nepříjemností je to, že se nám všechny tabulky spojí dohromady. Můžeme ji odstranit tím, že na konci prvního cyklu vytiskneme oddělovací čáru:
>>> for nasobitel in range(2, 13): ... for j in range(1, 13): ... print "%d x %d = %d" % (j, nasobitel, j*nasobitel) ... print "-------------------"
Všimněte si, že druhý příkaz print je odsazený o stejnou
hodnotu, jako řádek s druhým cyklem for — jde tedy o
druhý příkaz v posloupnosti příkazů cyklu. (Prvním příkazem je zde vnořený
cyklus.) Zapamatujte si, že úroveň odsazení je v jazyce Python velmi
důležitá.
Pokuste se vytvořit oddělovač tabulek, který by říkal, jaká tabulka mu
předchází — popis pod tabulkou. Nápověda: Pravděpodobně budete muset
použít proměnnou nasobitel a formátovací řetězec.
Některé jazyky umožňují více typů konstrukcí cyklu, ale obvykle podporují
něco jako for a while. (Modula 2 a Oberon
poskytují pouze cykly typu while, protože cykly
for jimi můžeme nasimulovat — jak jsme viděli výše.) Jiné
typy cyklů, se kterými se můžete setkat jsou:
while, ale test se provádí
až na konci, za tělem
cyklu. To znamená, že se cyklus provede vždy alespoň jednou.Zapamatujte si
FOR opakuje sadu příkazů při pevně daném počtu
iterací (obrátek cyklu).WHILE opakuje sadu příkazů dokud je splněna
podmínka. Může se stát, že se tělo cyklu neprovede nikdy. Stane se
tak v případě, kdy se pokračovací podmínka hned na začátku vyhodnotí jako
false (nepravda).FOR a WHILE
jsou k dispozici téměř ve všech jazycích.for v jazyce Python je skutečnosti cyklem typu
foreach — pracuje nad položkami seznamu.Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím tohoto odkazu. Tím bude do subjektu dopisu automaticky vložena informace o jméně a verzi tohoto HTML dokumentu.
$Id: cztutloops.html,v 1.2 2004/05/14 10:22:46 prikryl Exp $