Hlavní navigace

Programovací jazyk TCL (2)

26. 7. 2005
Doba čtení: 11 minut

Sdílet

V dnešním dílu seriálu o programovacím jazyku Tcl se budeme věnovat základním jazykovým konstrukcím - matematickým, logickým a bitovým operacím, matematickým funkcím a způsobu zápisu nových funkcí. Také si ukážeme postup provádění substitucí příkazů i proměnných.

Obsah

1. Několik slov úvodem
2. Zápis poznámek do zdrojového kódu
3. Práce s proměnnými
4. Substituce prováděné při běhu programu
5. Matematické operace a funkce
6. Logické a bitově orientované operace
7. Funkce
8. Obsah dalšího pokračování tohoto seriálu

1. Několik slov úvodem

V dalším textu budu uvádět poměrně velké množství demonstračních příkladů. Pokud máte na svém systému jazyk Tcl již nainstalován, můžete si tyto příklady ihned zkoušet. Možností pro spuštění programů je více: zápisem textu příkladu do souboru v některém textovém editoru s následným přímým spuštěním či spuštěním v interpreteru pomocí příkazu source, použitím interaktivního shellu tclsh a použitím grafického shelluwish. Pro první pokusy s jazykem Tcl doporučuji použití interaktivního shellu tclsh (jedná se o spustitelný program s řádkově orientovaným uživatelským rozhraním). Tento shell se ukončuje příkazem exit, násilně pak stejně jako každý jiný běžný proces operačního systému :-).

Pokud jazyk Tcl nemáte nainstalovaný, doporučuji pro jeho stažení použít stránku se snadno zapamatovatelným názvem www.tcl.tk. Pro operační systémy Microsoft Windows je Tcl k dispozici na stránce www.activesta­te.com. Šťastní majitelé počítačů Apple mohou navštívit stránky www.tcl.tk/sof­tware/mac, kde se dozví potřebné informace o provozu Tcl na svých strojích.

2. Zápis poznámek do zdrojového kódu

úvodní části tohoto seriálu jsme si řekli, že u programu napsaného v jazyce Tcl se všechna slova považují za řetězce, přičemž určitá slova představují příkazy a další slova parametry těchto příkazů. Příkazy jsou od sebe odděleny buď prázdným řádkem, nebo znakem ; (středník). Nejinak je tomu i u poznámek, které také začínají specifikovaným slovem, nejedná se tedy o žádnou specializovanou jazykovou konstrukci (s tou výjimkou, že se v textu poznámky neprovádí substituce příkazů ani proměnných – viz třetí kapitolu). Poznámka začíná, podobně jako u skriptů pro shell či Perl, slovem/řetězcem o jednom písmenu – # (křížek). Toto slovo se při svém „spuštění“ chová tak, že všechny další znaky až do konce řádku přečte a ignoruje. Ve skutečnosti k tomuto zpracování poznámek dochází už při načítaní programu lexikálním analyzátorem, to je však pouze užitečná pomůcka pro rychlejší běh programu. Příklad jednoduché poznámky:

# toto je moje poznámka 

Při zápisu poznámek je zapotřebí pamatovat na jedno důležité pravidlo: vzhledem k tomu, že je začátek poznámky považován za příkaz, musí poznámka buď začínat na novém řádku, nebo musí být (pokud je na stejném řádku s příkazem) od předchozího příkazu oddělena středníkem. Správně by tedy měla být poznámka spolu s příkazem na jednom řádku zapsána následovně:

puts ahoj; # vypíše řetězec "ahoj" 

Špatný (i když na první pohled korektní) zápis má tvar:

puts ahoj  # vypíše řetězec "ahoj" 

3. Práce s proměnnými

Práce s proměnnými v Tcl je velmi jednoduchá a přitom propracovaná. Platí pouze jedno pravidlo, které je nutné dodržet. Toto pravidlo říká, že proměnná musí být před svým prvním použitím inicializována (to je mnohem lepší než povolení používání neinicializovaných proměnných, jak to známe z Perlu či Basicu, či dosazování implicitní hodnoty do proměnných, což zase provádí C a od něj odvozené jazyky). Inicializace proměnné se provádí příkazem set, za nímž se uvede jméno proměnné a její hodnota (ta může být vyjádřena buď konstantou, nebo příkazem/výrazem, který se vyhodnotí). Příklad vytvoření a současně inicializace dvou proměnných:

set answer 42
set hello "Hello world" 

Proměnná answer je inicializována hodnotou 42, proměnná hello hodnotou „Hello world“ (bez uvozovek). Hodnoty obou proměnných jsou přitom považovány za řetězce – žádný jiný datový typ ostatně Tcl nezná. Uvozovky zde musely být použity proto, aby se zpracovalo i slovo „world“, které je od předchozího slova odděleno mezerou. V případě, že se do proměnné ukládá řetězec neobsahující mezery, nemusí se uvozovky psát:

set os Linux 

Pokud vám Tcl vzdáleně připomíná Lisp (což je správný postřeh), vězte, že zde nelze, na rozdíl od Lispu, nastavit jedním příkazem set více proměnných. Zatímco v Lispu by byl následující příkaz korektní:

(setq answer 42 hello "Hello world") 

v Tcl je podobný příkaz chybný:

set answer 42 hello "Hello world" 

Inicializaci však lze provést na jednom řádku – zde se použije středník, který odděluje jednotlivé příkazy:

set answer 42 ; set hello "Hello world" 

Pokud se příkaz set zadá pouze s názvem proměnné, vrátí se její hodnota. Tato hodnota se v případě interaktivní práce s tclsh vypíše na obrazovku:

set answer
set hello
set os
set xxx ; # zde se vypíše chybové hlášení, protože tato proměnná neexistuje 

O zrušení proměnné se stará příkaz unset, kterému se předá název proměnné. Po provedení tohoto příkazu je proměnná uvolněna z paměti, což je, zvláště v případě, kdy se používají rozsáhlá asociativní pole či seznamy, mnohdy velmi důležitá operace:

unset answer
unset hello
unset os 

Hodnotu proměnné lze vytisknout na terminál (přesněji řečeno na standardní výstup) příkazem puts, za který se dá název proměnné (dříve zmíněný příkaz set vypíše hodnotu pouze při interaktivní práci v shellu). Při použití příkazu puts se poprvé setkáme s takzvanou substitucí, která spočívá v tom, že v některých případech interpreteru Tcl řekneme, zda má použít jméno proměnné (což je řetězec), nebo její hodnotu. Pokud potřebujeme získat hodnotu proměnné, musí se před její jméno vložit znak $ (dolar), který způsobí substituci jména proměnné za její hodnotu. Vyzkoušejte si následující příklad a rozdíly ihned zjistíte z výpisu, který skript vytvoří:

set answer 42
set hello "Hello world"
puts answer
puts hello
puts $answer
puts $hello 

Poznámka ke znaku dolaru: za dob socialismu se z mně neznámých důvodů všude potlačovaly symboly „kapitalistického“ světa. Z toho důvodu mělo velké množství tiskáren a dálnopisů vyráběných v zemích RVHP na místě, kde je v ASCII kódu dolar (pozice 36), znak „ruského dolaru“ (tak tomu říkali starší kolegové), což je takové kolečko se čtyřmi nožkami, které znamená obecnou měnu. Vypadá to nějak takto: ¤. Takže pokud někde ve sklepě či na půdě výpočetního střediska naleznete starý listing programů psaných ve Fortranu, Forthu či Basicu (tam to vadilo nejvíc), vězte, že to kolečko je vlastně dolar, i když si za něj nic nekoupíte :-).

4. Substituce prováděné při běhu programu

Už v předchozí kapitole jsme se částečně zmínili o substitucích. Substituce se v Tcl používají například k nahrazení jména proměnné její hodnotou – zde se používá onen zmíněný dolar (pro cca desetinu naší populace kolečko). V některých případech by se nám hodilo substituci zakázat. To je samozřejmě možné a děje se tak použitím složených závorek – { a}. Rozdíl mezi použitím složených závorek zakazujících substituci a uvozovek umožňujících zápis řetězce s mezerami ukazuje následující příklad. V něm je nejdříve inicializována proměnná answer, a tato proměnná je použita při tisku dvou řetězců. V prvním případě se provede substituce jména proměnné na její hodnotu, ve druhém případě se řetězec vytiskne beze změny tak, jak byl zadán:

set answer 42
puts "Odpověď je $answer"
puts {Odpověď je $answer} 

Další možností je zrušení speciální funkce znaku $ tím, že se před něj vloží zpětné lomítko:

puts "Odpověď je \$answer" 

Jak si ukážeme v dalších odstavcích, jsou složené závorky často použity například při zápisu těla smyček. Je to logické – tělo smyčky se nesmí provést již při jejím vytváření, ale až po jejím spuštění. Z tohoto důvodu je nutné prvotní provedení smyčky (která v tomto případě znamená zavolání prvního příkazu v těle smyčky) zakázat.

V mnoha případech je nutné, aby se vykonal nějaký příkaz s tím, že se jeho návratová hodnota předá do dalšího příkazu. V Tcl se tato operace provede tak, že se kód příkazu i s jeho parametry uzavře do hranatých závorek – [ a ]. Kód, který je v těchto závorkách uzavřen, se provede nejdříve a výsledek se vrátí nadřazenému příkazu (ze sémantického hlediska se jedná o kompozici funkcí). Počet takto vnořených příkazů není omezen:

příkaz1 [příkaz2 argument argument ...] argument ...
příkaz1 [příkaz2 argument [příkaz 3 argument ...] argument ...] argument ... 

Konkrétní příklad na použití složených závorek si uvedeme v následující kapitole. Posledním typem substituce je využití zpětného lomítka, které ruší zvláštní význam následujícího znaku. Zpětné lomítko se většinou používá pro „vyrušení“ významu dolaru a znaku pro konec řádku – umožňuje tedy vytvářet víceřádkové příkazy i v místech, kde by to jinak nebylo možné.

Nyní si shrňme všechny prováděné substituce (a jejich opaky) do jedné tabulky:

Použité znaky Význam znaků
$ substituce proměnných – náhrada jména proměnné její hodnotou
[] vyhodnocení příkazu – příkaz v závorkách se vyhodnotí nejdříve
"" potlačuje zpracování mezer jako oddělovačů příkazů či jejich argumentů
{} stejné jako uvozovky, s tím rozdílem, že se všechny substituce uvnitř závorek zakazují
\ ruší zvláštní význam následujícího znaku

Další rozdíl mezi zpracováním slov umístěných mezi znaky "" a {} spočívá v tom, že se uvnitř složených závorek mohou objevovat i znaky konce řádku.

5. Matematické operace a funkce

Na začátku povídání o matematických operacích a funkcích je zapotřebí říci, že jazyk Tcl neobsahuje žádné speciální konstrukce pro zápis matematických operací. Místo toho má k dispozici příkaz (tj. funkci), kterému je možné předložit zápis matematického výrazu, a návratovou hodnotou je výsledek daného výrazu. Tento příkaz se jmenuje expr a jeho možnosti jsou opravdu veliké – k dispozici je dokonce i podmíněný ternární operátor známý z jazyka C. Před dalším popisem podporovaných matematických operací si ukažme příklad použití příkazu expr:

expr 1 + 2
expr 10.0 / 5
expr 1 << 10
expr $a + $b 

Jak je z předchozího příkladu patrné, je možné v příkazu expr používat jak číselné konstanty (tyto mohou být celočíselné i reálné), tak i proměnné. Již minule jsme si říkali, že všechny argumenty příkazů by měly být odděleny alespoň jedním bílým znakem, příkaz expr je však v tomto směru výjimkou. Pokud jednotlivé části výrazu nebudou bílými znaky odděleny, budou se chápat jako řetězec, který se však po zavolání příkazu expr snadno rozloží na své části (zde však již mezitím došlo k substituci proměnných). Předchozí výpočty lze tedy napsat i následujícím „zkráceným“ způsobem:

expr 1+2
expr 10.0/5
expr 1<<10
expr $a+$b 

Příkaz expr lze samozřejmě použít i společně s dalšími příkazy, hojně se zde využívá hranatých závorek ovlivňujících prioritu provádění jednotlivých příkazů:

set a [expr 1+2]
set b 20
set c "$a + $b se rovná [expr $a + $b]"
set c {$a + $b se rovná [expr $a + $b]}; # zde nedojde k substituci 

Mezi podporované matematické operace patří:

Operace Význam
+ unární plus
unární mínus
+ součet (dále se již jedná o binární operace)
rozdíl
* součin
/ podíl
% zbytek po dělení

Kromě výše zmíněných operací je k dispozici velké množství matematických funkcí, například:

acos floor sin
asin fmod sinh
atan hypot sqrt
atan2 log tan
ceil log10 tanh
cos pow

6. Logické a bitově orientované operace

Zápis logických a bitově orientovaných operací vychází z jazyka C, o čemž se ostatně můžete přesvědčit z následující tabulky:

Operace Význam
<< bitový posun doleva
>> bitový posun doprava
~ negace bit po bitu
! logická negace
& bitový operátor AND
| bitový operátor OR
^ bitový operátor XOR
&& logický součin
|| logický součet
?: ternární podmíněný výraz

K logickým operacím lze přidat i relační operátory, které taktéž vycházejí z programovacího jazyka C:

Operace Význam
< menší než
> větší než
<= menší nebo rovno
>= větší nebo rovno
== rovnost
!= nerovnost

7. Funkce

Funkce (resp. příkazy či procedury) používané v Tcl mohou být tří typů: vestavěné funkce, funkce vytvořené příkazem proc a externí funkce, vytvořené například v programovacím jazyku C. Funkce všech tří typů mohou mít libovolný počet parametrů (ten se dokonce může měnit) a mohou vracet nějakou hodnotu. Pro vytvoření nové funkce se používá příkaz/funkce­proc, za nímž se uvede název nové funkce, její parametry (ty se uvnitř funkce chovají jako proměnné) a tělo funkce (tj. jednotlivé příkazy, ze kterých se funkce skládá). Funkce může být při svém zavolání kdykoli přerušena příkazem return, který může vracet nějakou hodnotu. Pokud se funkce ukončí jiným příkazem než return, vrátí se hodnota posledně prováděného příkazu (návratovou hodnotu je samozřejmě možné ignorovat). Příklad jednoduché funkce bez parametrů:

proc hello {} {
    puts "Hello world"
} 

Všimněte si zápisu těla funkce do složených závorek. To je v tomto případě nutné, protože jinak by se tělo funkce vyhodnotilo již při jejím vytvoření. Dále je nutné, aby druhá otevírací složená závorka byla na stejném řádku jako příkaz proc – všechny příkazy v Tcl jsou řádkově orientované. Příkazy uvnitř těla funkce se již mohou formátovat libovolně, protože uvnitř složených závorek je povoleno používat znak konce řádku.

Příklad funkce se dvěma parametry vracející výsledek:

proc secti {a b} {
    return [expr $a+$b]
} 

Parametry funkce mohou být zcela libovolné, takže je možné předávat i operátory či příkazy, jak si ukážeme na následujícím příkladu. Mějme funkci tabulka_and, která při svém zavolání vytiskne pravdivostní tabulku funkce AND:

proc tabulka_and {} {
    puts [expr 0 & 0]
    puts [expr 0 & 1]
    puts [expr 1 & 0]
    puts [expr 1 & 1]
} 

Funkce tabulka_and však není moc univerzální – funguje pouze s jednou pevně nastavenou logickou funkcí, v našem případě logickým součinem. Pokusme se vytvořit jinou funkci, která toto omezení již nebude mít. Využijeme přitom faktu, že každý parametr funkce je chápán jako řetězec, takže je pomocí něj možné předat i textovou podobu operátoru, který se potom ve funkci díky substitucím použije:

proc log_funkce {operator} {
    puts [expr 0 $operator 0]
    puts [expr 0 $operator 1]
    puts [expr 1 $operator 0]
    puts [expr 1 $operator 1]
} 

Příklad použití funkce log_funkce:

CS24_early

log_funkce &
log_funkce |
log_funkce ^ 

8. Obsah dalšího pokračování tohoto seriálu

V další části tohoto seriálu bude popsána práce s podmínkami, smyčkami a se strukturovanými datovými typy, zejména s asociativními poli a seznamy.

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.