Obsah
1. Podmíněné příkazy
2. Iterační příkazy – cykly
3. Strukturované datové typy
4. Asociativní pole
5. Seznamy
6. Obsah dalšího pokračování tohoto seriálu
1. Podmíněné příkazy
V prakticky každém imperativním programovacím jazyce (samozřejmě pokud nepoužívá speciální formy) jsou k dispozici příkazy, kterými je možné rozvětvit běh programu v závislosti na nějaké podmínce. Nejinak je tomu i ve zde popisovaném programovacím jazyku Tcl. Ten obsahuje hned dvě varianty větvení: strukturovaný příkaz typu if-then-else a příkaz typu switch. Nejprve si popíšeme první variantu, tj. příkaz typu if-then-else. Nejjednodušší forma tohoto příkazu vypadá následovně:
if podmínka tělo_podmínky
Podmínka (která většinou musí být kvůli zákazu předčasného vyhodnocení umístěna do složených závorek) se vyhodnocuje stejným způsobem jako v případě minule popsaného příkazu expr, tj. je možné použít všechny dostupné matematické, logické i relační operátory, stejně jako jednoduché (skalární) proměnné. Výsledkem vyhodnocené podmínky je řetězec, který je interně převeden na booleovskou proměnnou. Pokud tento řetězec obsahuje nenulové číslo, hodnotu „true“ nebo hodnotu „yes“, je interně převeden na logickou hodnotu „true“ a následně se provede tělo podmínky umístěné za příkazem if (to je taktéž ve většině případů uzavřeno do složených závorek). Pokud řetězec obsahuje jinou hodnotu (včetně nuly), tělo podmínky se neprovede a program pokračuje ve svém běhu příkazem umístěným za podmíněným příkazem.
Prozatím jsme si uvedli tu nejjednodušší formu podmíněného příkazu. Jazyk Tcl je však v tomto případě poměrně variabilní a podporuje i další způsoby vytváření podmínek. První – ve své podstatě pouze podpůrnou – variantou je uvedení slova then přímo za výraz specifikující podmínku. Toto klíčové slovo je totiž použito v mnoha programovacích jazycích (z ne-Cčkovské rodiny, jedná se například o jazyky založené na Algolu), proto jsou pro snadný přechod z dalších jazyků na Tcl podporovány obě varianty. Podmíněný příkaz se slovem then lze zapsat následovně (připomínám, že v případě jazyka Tcl se nejedná o klíčové slovo – tento jazyk pojem klíčového slova vůbec nezná a ani nepotřebuje):
if podmínka then tělo_podmínky
Další variantou je, jak jistě sami očekáváte, přidání druhé větve příkazů. Příkazy ve druhé větvi se vykonají v případě, že podmínka není splněna, tj. vyhodnotí se na logickou hodnotu false. I zde je možné (nikoli však nutné) použít slovo then, slovo else uvozující druhou větev také nemusí být použito:
if podmínka tělo_podmínky tělo_druhé_větve
if podmínka tělo_podmínky else tělo_druhé_větve
if podmínka then tělo_podmínky tělo_druhé_větve
if podmínka then tělo_druhé_větve else tělo_druhé_větve
To však není zdaleka vše. Vzhledem k tomu, že se v reálných problémech nepoužívají pouze jednoduché podmíněné výrazy, ale složitější struktury, obsahuje Tcl rozšíření pro zápis více podmíněných výrazů a větví v jednom bloku if. Jedná se o vícenásobně použité slovo elseif, za nímž vždy následuje vyhodnocovaný výraz a tělo (blok příkazů), které se provede v případě, že je výraz vyhodnocen jako pravdivý. Vše je patrné z následujících schémat:
if podmínka tělo1 elseif podmínka2 tělo2 tělo_else
if podmínka then tělo1 elseif podmínka2 then tělo2 else tělo_else
Na tomto místě musím upozornit na fakt, že slovo elseif je nutné uvádět, nelze s ním tedy zacházet tak liberálně jako se slovy then a else.
Při zápisu podmíněného příkazu na více řádků je zapotřebí zaručit, aby se začátek případné větve else nacházel na stejném řádku, kde je uzavírací závorka větve then. Je to z toho důvodu, že Tcl celý skript zpracovává po jednotlivých řádcích a v případě, že by řádek začínal přímo slovem else, považoval by ho interpreter za začátek dalšího příkazu. Následující příklad je tedy korektní:
if {výraz} {
tělo_podmínky
} else {
tělo_druhé_větve
}
Stejné pravidlo platí pro zápis dvou větví bez slova else:
if {výraz} {
tělo_podmínky
} {
tělo_druhé_větve
}
Zatímco podmíněný příkaz typu if-then-else je vcelku běžný, příkaz typu switch už tak obyčejný není, jak by se mohlo podle jeho podobnosti s dalšími programovacími jazyky znát. Vzhledem k tomu, že všechny hodnoty jsou v Tcl reprezentovány pomocí řetězců, porovnávají se v příkazu switch právě řetězce, přičemž je možné specifikovat porovnávání na základě regulárních výrazů, což celý příkaz switch posouvá na mnohem vyšší úroveň použitelnosti než běžné příkazy switch a case známé z jiných programovacích jazyků. Obecný zápis rozvětvení pomocí příkazu switch vypadá takto:
switch řetězec vzor1 větev1 vzor2 větev2 ... default větev_n
Při praktickém použití jsou jednotlivé větve (tj. bloky příkazů) uzavřeny do složených závorek, stejně jako tomu bylo u podmíněného příkazu if-then-else. Podrobnější informace budou uvedeny v další části tohoto seriálu.
2. Iterační příkazy – cykly
Kromě podmíněných příkazů je možné řídit průběh programu pomocí iteračních příkazů, tj. příkazů, kterými lze programovat cykly. Základní a zcela univerzální smyčkou je v mnoha jazycích smyčka while. Nejinak je tomu v Tcl, kde lze smyčku typu while zapsat podle následujícího schématu:
while podmínka telo_smyčky
Tato smyčka pracuje podobným způsobem jako stejně pojmenovaná smyčka v jazycích, jako je C, C++, Java, Pascal nebo Visual Basic (v posledním případě se jedná o smyčku do while). Před prvním provedením smyčky se vyhodnocuje podmínka stejným způsobem jako u podmíněného příkazu if. Pokud je výsledkem vyhodnocení podmínky logická hodnota „true“, vykoná se jedna iterace a řízení programu se vrátí na začátek smyčky, tj. na test podmínky. Pokud se je výsledkem vyhodnocení podmínky logická hodnota „false“, pokračuje běh programu příkazem uvedeným za smyčkou. Následuje příklad jednoduché smyčky, jejíž běh je řízen proměnnou i. Smyčka se provede celkem devětkrát:
set i 1
while {$i<10} {
puts $i
set i [expr $i+1]
}
Jak je z předchozího příkladu patrné, není smyčka moc přehledná, zejména vinou složitého příkazu pro zvýšení hodnoty proměnné i o jedničku. Kvůli zjednodušení zápisu smyček (ale i v jiných případech) proto v Tcl existuje příkaz incr, který zvýší či sníží hodnotu proměnné o zadané číslo. Pokud není žádné číslo v příkazu incr zadáno, zvýší se implicitně hodnota proměnné o jedničku. Předchozí příklad je tedy možné přehledněji zapsat následovně:
set i 1
while {$i<10} {
puts $i
incr i
}
Můžeme si vyzkoušet, co se stane, pokud specifikujeme číslo, o které se hodnota proměnné bude zvyšovat. Po spuštění dalšího příkladu se na konzoli vypíšou všechna sudá čísla od dvou do dvaceti:
set i 2
while {$i<=20} {
puts $i
incr i 2
}
Zpětné počítání je také jednoduché, postačí specifikovat záporný krok:
set i 10
while {$i} {
puts $i
incr i -1
}
Použití smyčky while si můžeme ukázat na jednoduchém příkladu, ve kterém je vytvořena funkce, pomocí níž je možné spočítat celočíselnou mocninu libovolného základu.
proc power {base p} {
set result 1
while {$p} {
set result [expr $result * $base]
incr p -1
}
return $result
}
Vzhledem ke způsobu převodu řetězců na čísla při matematických výpočtech si musíme dát pozor na rozdíl mezi zavoláním funkce „power 2 100“ a „power 2.0 100“. V prvním případě nebude výsledek správný, neboť se bude počítat s čísly typu integer, ve druhém případě se výpočet provede s čísly typu float (je škoda, že Tcl nepoužívá knihovnu pro práci s velkými čísly tak, jako to dělá například Lisp, bc nebo dc – byla by tak zaručena větší přenositelnost skriptů mezi různými platformami).
Malou úpravou výše uvedeného příkladu je výpočet faktoriálu, který lze zapsat následovně:
proc fact {n} {
set result 1.0
if {$n<0} {return 1}
while {$n} {
set result [expr $result*$n]
incr n -1
}
return $result
}
Všimněte si způsobu, jakým si vynucujeme výpočty v pohyblivé řádové čárce – do proměnné result je vložena hodnota 1.0, která způsobí to, že dostáváme korektní výsledky pro všechna n menší než 171 (platí pro platformu i386).
Kromě smyčky while je možné použít i „počítanou“ smyčku for, která zde má stejný význam jako v programovacím jazyku C. Tuto smyčku je možné zapsat podle následujícího schématu:
for start test iterační_příkaz tělo_smyčky
V reálných programech bývají všechny čtyři části smyčky for zapsány ve složených závorkách, aby se zamezilo předčasnému vyhodnocení příkazů. Výpočet faktoriálu je možné pomocí smyčky for zapsat takto:
proc fact2 {n} {
set result 1.0
for {set i $n} {$i} {incr i -1} {
set result [expr $result*$i]
}
}
Vidíme, že zápis je nejen kratší, ale i přehlednější, protože všechny příkazy, které smyčku „opečovávají“, jsou umístěny u sebe na jednom řádku. Podobně jako v jazyku C lze i zde využít toho, že nulová hodnota (resp. řetězec obsahující nulu) se považuje za logickou hodnotu „false“ a nenulová hodnota za logickou hodnotu „true“.
V obou uvedených smyčkách je možné použít příkazy break a continue. Příkazem break se může smyčka v libovolném místě předčasně ukončit. Příkaz continue způsobí skok na začátek smyčky.
Posledním popisovaným typem smyčky je smyčka foreach, kterou lze použít při průchodu seznamem. Bližší informace o této smyčce budou uvedeny v sedmé kapitole.
3. Strukturované datové typy
Programovací jazyk Tcl umožňuje základní práci pouze se skalárními hodnotami. Vzhledem ke značné rozšiřitelnosti jazyka však není problémem pracovat i se strukturovanými datovými typy. Mezi tyto datové typy patří zejména asociativní pole a seznamy. Práce s asociativními poli je popsána ve čtvrté kapitole, práce se seznamy v kapitole páté. Zajímavé je, že rozšíření jazyka o tyto datové typy se obešlo bez zásahu do interpreteru, pouze se zavedlo několik nových funkcí/příkazů.
4. Asociativní pole
Jazyk Tcl, podobně jako další imperativní programovací jazyky, umožňuje práci s poli. Nejedná se však o pole indexovaná číselnými hodnotami, ale o asociativní pole, kde je klíčem obecně libovolný řetězec (asociativní pole byla po dlouhou dobu známá jako hashe – viz například programovací jazyk Perl či JavaScript). Nejprve si ukážeme použití polí indexovaných čísly (tak to známe z nižších programovacích jazyků); musíme však mít na paměti, že číselné hodnoty se interně převádějí na řetězce. Pole jsou jako celek uložena také ve formě řetězce, interní formát nás však většinou nemusí zajímat:
set pole(0) 100
set pole(1) 150
set pole(9) "bla bla"
puts $pole(0)
puts $pole(1)
puts $pole(9)
Jak jsme si již řekli v předchozím odstavci, lze jako klíč použít libovolný řetězec, což je ukázáno na dalším příkladu s typickým využitím asociativních polí pro tvorbu velmi jednoduchého slovníku:
set slovnik(pocitac) computer
set slovnik(mys) mouse
set slovnik(skok) jump
Pole lze samozřejmě plnit i pomocí smyčky:
while {$i<10} {set a($i) $i; incr i}
Pro práci s poli je k dispozici několik funkcí, z nichž ty nejvýznamnější jsou uvedeny v následující tabulce. Všechny funkce začínají příkazem array, který jako svůj první parametr vyžaduje název operace, jež se má s polem provést:
Název funkce | Význam funkce |
---|---|
array get pole | vrací všechny hodnoty klíčů i prvků pole |
array get pole vzor | vrací všechny hodnoty klíčů i prvků pole, kde klíče odpovídají zadanému vzoru |
array names pole | vrací všechny klíče (indexy) pole |
array names pole vzor | vrací všechny klíče pole, které odpovídají zadanému vzoru |
array set pole seznam | vytváří pole ze seznamu (indexuje se automaticky) |
array exists pole | provede ověření (predikát), zda existuje pole o zadaném názvu – vrací řetězec 0 nebo 1 |
array size pole | vrátí počet prvků v poli – vhodné pro počítané smyčky |
Za povšimnutí stojí příkaz array get pole, kterým je možné převádět pole na seznam. Kromě výše zmíněných příkazů lze vyhledávat prvky v poli pomocí voleb startsearch, donesearch a nextelement. V některých případech je však vhodnější převést pole na seznam a procházet seznamem pomocí smyčky foreach.
5. Seznamy
Kromě polí je v Tcl k dispozici i datový typ seznam. Seznam se od polí liší zejména v tom, že jeho prvky mohou být libovolného typu, například další seznamy. Rekurzivním vkládáním seznamů do sebe lze z původně lineární datové struktury vytvořit například binární či n-ární strom. Seznam se vytvoří velmi jednoduše pomocí příkazu set:
set seznam1 { 1 2 3 4 5 6 }
set seznam2 { jedna dve tri ctyri pet sest }
set seznam3 { 1 {2 3} {4 5} {6 7} {8} }
Všimněte si zde faktu, že pro vytvoření seznamu není zapotřebí žádná další jazyková konstrukce – vystačíme si s konstrukcemi uvedenými v předchozí části tohoto seriálu. I to ukazuje, jak je na první pohled jednoduchý (až triviální) jazyk při praktickém používání flexibilní (seznamy už vlastně známe, neboť se používaly při zápisu bloků kódu ve smyčkách a podmínkách). Pro práci se seznamy je k dispozici mnoho funkcí, z nichž některé jsou uvedeny v následující tabulce (způsob zápisu funkcí je podobný funkcím pro práci s poli):
Název funkce | Význam funkce |
---|---|
list | vytvoření seznamu z argumentů, které jsou tomuto příkazu zadány |
concat | spojení dvou a více seznamů |
llength | získání počtu prvků v seznamu |
split | rozložení řetězce na seznam buď podle bílých znaků nebo podle specifikovaného oddělovače |
join | vytvoření řetězce spojením prvků seznamu, mezi něž se může volitelně vložit oddělovač |
lappend | přidání jednoho či více prvků do seznamu |
linsert | vložení jednoho či více prvků na danou pozici (index) |
lindex | získání prvku ze seznamu na dané pozici (indexu) |
lreplace | nahrazení prvků v seznamu |
lrange | vyjmutí více prvků ze seznamu (souvislá oblast) |
lsearch | hledání prvků v seznamu (možné i podle regulárních výrazů!) |
lsort | setřídění prvků v seznamu podle zadaných kritérií |
Pro průchod seznamem je samozřejmě možné použít smyčky while či for – stačí využít funkce llength a lindex. To však není příliš efektivní ani přehledné, proto byla do Tcl přidána speciální forma smyčky, která se používá právě při práci se seznamy. Tato smyčka se jmenuje foreach a její použití se v nejjednodušší podobě řídí podle schématu:
foreach proměnná seznam tělo_smyčky
Podobně jako u smyčky for se i zde používá řídicí proměnná. Tato proměnná však nenabývá číselných hodnot, ale jsou do ní postupně dosazovány jednotlivé prvky seznamu. Příklad použití této smyčky:
foreach i {jedna dve tri ctyri} {
puts $i
}
Ve složitějších případech je možné použít i seznam názvů proměnných, do kterých se při běhu smyčky budou postupně dosazovat sousední prvky:
foreach {i j} {1 2 3 4 5 6} {
puts "i= $i"
puts "j= $j"
}
Poslední modifikací smyčky foreach je specifikace jedné či více proměnných, kde každá proměnná bude použita pro svůj vlastní seznam:
foreach i {1 2 3} j {4 5 6}
puts "i= $i"
puts "j= $j"
}
6. Obsah dalšího pokračování tohoto seriálu
V dalším pokračování tohoto seriálu dokončíme část věnovanou programovacímu jazyku Tcl (dále již bude popisována knihovna Tk). Budeme se věnovat práci s řetězci, regulárním výrazům atd.