Hlavní navigace

Matematika v příkazové řádce VII - utilita calc (2)

7. 3. 2006
Doba čtení: 11 minut

Sdílet

V sedmém pokračování seriálu věnovaného matematicky zaměřeným aplikacím provozovaným zejména z příkazového řádku budeme pokračovat v popisu již minule zmiňované utility calc. Popíšeme si tvorbu podmíněných příkazů, počítaných i nepočítaných smyček, způsob vytváření nových funkcí a další důležité a zajímavé vlastnosti calcu, které je možné využít při psaní jednoduchých i rozsáhlých skriptů pro tuto aplikaci.

Obsah

1. Podmíněný příkaz typu if-then-else
2. Podmíněný příkaz typu switch
3. Nepočítaná smyčka typu while
4. Nepočítaná smyčka typu do-while
5. Počítaná smyčka typu for
6. Vytváření nových funkcí
7. Obsah dalšího pokračování tohoto seriálu

1. Podmíněný příkaz typu if-then-else

Již v předchozím pokračování tohoto seriálu jsme si řekli, že syntaxe použitá ve skriptech (tj. interpre­tovaných programech) určených pro utilitu calc se do značné míry podobá syntaxi programovacího jazyka C a od něj odvozených jazyků, mezi něž patří například C++, Java, C#, PHP apod. Platí to samozřejmě i pro syntaxi podmíněných příkazů. Základem podmíněných příkazů je v mnoha programovacích jazycích příkaz typu if-then-else. Tento příkaz má ve své zkrácené podobě, tj. pokud je použita pouze část if-then bez větve else, ve skriptech určených pro calc následující tvar:

if (výraz) příkaz 

V případě, že je hodnota vyhodnoceného výrazu výraz nenulová (což je vyhodnoceno jako pravdivostní hodnota true), provede se zadaný příkaz. Naopak, pokud je hodnota vyhodnoceného výrazu výraz nulová (to odpovídá pravdivostní hodnotě false), podmíněný příkaz uvedený za if se neprovede. Jestliže je podmínka složitější a je potřeba místo jednoho příkazu v podmínce uvést příkazů více, je možné, podobně jako v céčku, použít takzvaný příkazový blok, který začíná levou složenou závorkou a končí pravou složenou závorkou. V příkazovém bloku jsou od sebe jednotlivé příkazy odděleny středníkem, tj.:

if (výraz) {
    příkaz1;
    příkaz2;
    příkaz3;
    ...
} 

Úplná varianta příkazu if i s větví else má ve své nejjednodušší podobě tvar:

if (výraz) příkaz1 else příkaz2 

Výše uvedený podmíněný příkaz se při běhu programu chová následovně: pokud je hodnota vyhodnoceného výrazu výraz nenulová (tj. vyhodnotí se jako pravdivostní hodnota true), provede se příkaz příkaz1, v opačném případě (tj. výraz se vyhodnotí na pravdivostní hodnotu false) se provede příkaz příkaz2. Samozřejmě i zde je možné použít příkazové bloky a rozšířit tak možnosti podmínek na prakticky libovolné množství příkazů:

if (výraz) {
    příkaz1;
    příkaz2;
    příkaz3;
    ...
} else {
    příkazX;
    příkazY;
    příkazZ;
    ...
} 

Zejména v interaktivním režimu je zapotřebí dbát na to, aby začátek větve else ležel na stejném řádku jako levá uzavírací závorka první větve; v případě nesplnění této podmínky se vypíše varování o použití příkazu else bez odpovídajícího příkazu if, tj. klíčové slovo else na samostatné řádce není spárováno s předešlým příkazem if – to je jedna z odlišností od syntaxe programovacího jazyka C, jehož způsob zápisu podmíněného příkazu je volnější. Po provedení jedné z větví příkazu if se pokračuje v běhu programu příkazem, který za if následuje. Následují tři velmi jednoduché příklady ukazující použití podmíněného příkazu typu if (tučným písmem je označen vstup, normálním písmem výstup):

a=1
b=2
if (a<b) print "a je menší než b"; else print "a je větší než b";
a je menší než b

a=2
b=3
c=4
det=b^2-4*a*c
if (det<0) print "Rovnice ma komplexní kořeny"; else print "Rovnice ma reálné kořeny"
Rovnice ma komplexní kořeny

a=1
b=4
c=1
det=b^2-4*a*c
if (det<0) print "Rovnice ma komplexní kořeny"; else print "Rovnice ma reálné kořeny"
Rovnice ma reálné kořeny 

2. Podmíněný příkaz typu switch

Kromě výše popsaného podmíněného příkazu typu if-then-else je možné ve skriptech určených pro calc používat i další typicky „céčkovský“ podmíněný příkaz. Jedná se o příkaz typu switch, který je v některých programovacích jazycích pojmenován case. Za povšimnutí stojí, že v už dříve popisované utilitě bc není tento typ podmíněného příkazu podporován, i když se taktéž jedná o nástroj se skripty syntakticky podobnými programovacímu jazyku C. V calcu slouží podmíněný příkaz typu switch k provedení rozeskoků podle hodnoty řídicího výrazu. Syntaxe tohoto příkazu je následující:

switch (výrazR) {
    case výraz1: příkazy1; break;
    case výraz2: příkazy2; break;
    case výraz3: příkazy3; break;
    default:     přikazyX; break;
} 

Při provádění této programové struktury je nejdříve vyhodnocena hodnota řídicího výrazu výrazR. Potom je vypočtená hodnota tohoto výrazu postupně porovnávána s hodnotami výrazů uvedenými za klíčovým slovem case, tj. s výrazy výraz1, výraz2 atd. Jestliže dojde ke shodě obou hodnot, jsou provedeny příslušné příkazy nacházející se za dvojtečkou. Další řízení programu závisí od toho, zda je za posledním příkazem ve větvi uvedeno klíčové slovo break, které slouží k opuštění tohoto podmíněného příkazu. Pokud slovo break není uvedeno, může řízení programu pokračovat s další větví case. Větev označená klíčovým slovem default se provede v případě, že není provedena žádná jiná větev příkazu switch.

Vzhledem k tomu, že céčkové programy jsou před spuštěním kompilovány, kdežto skripty určené pro calc jsou interpretovány až za běhu programu, bylo možné příkaz switch rozšířit o další funkcionalitu, která není v céčku dostupná. Prvním rozšířením je, že hodnoty výrazů nejsou omezeny pouze na čísla, ale mohou jimi být i další hodnoty. Další rozšíření spočívá v tom, že výrazy uvedené v jednotlivých větvích case nemusí být konstantní; může se jednat o výrazy, jejichž konkrétní hodnota je vypočtena v době běhu aplikace. Naproti tomu se v programovacím jazyce C musely tyto výrazy vyhodnotit na hodnotu již v době překladu, což komplikovalo práci, zejména při potřebě použít knihovní funkce (ty se samozřejmě v době překladu nevyhodnocují). Následuje jednoduchý příklad ukazující práci s podmíněným příkazem switch:

    switch (z) {
        case 0: v = (s == sgn(y)); break;
        case 1: v = (s == -sgn(y)); break;
        case 2: v = (s == sgn(x)); break;
        case 3: v = (s == -sgn(x)); break;
        case 4: v = (s > 0); break;
        case 5: v = (s < 0); break;
        case 6: v = (s == sgn(x/y)); break;
        case 7: v = (s == -sgn(x/y)); break;
        case 8: v = iseven(n); break;
        case 9: v = isodd(n); break;
        case 10: v = (x/y > 0) ? iseven(n) : isodd(n); break;
        case 11: v = (x/y > 0) ? isodd(n) : iseven(n); break;
        case 12: v = (y > 0) ? iseven(n) : isodd(n); break;
        case 13: v = (y > 0) ? isodd(n) : iseven(n); break;
        case 14: v = (x > 0) ? iseven(n) : isodd(n); break;
        case 15: v = (x > 0) ? isodd(n) : iseven(n); break;
    } 

Další příklad už je poněkud složitější, je na něm totiž vypsána malá část funkce strchar(). V úryvku kódu se transformují řídicí kódy ASCII na svoji symbolickou textovou reprezentaci.

    switch (a) {
        case 7: print "\\a":;
                return;
        case 8: print "\\b":;
                return;
        case 9: print "\\t":;
                return;
        case 10: print "\\n":;
                 return;
        case 11: print "\\v":;
                 return;
        case 12: print "\\f":;
                 return;
        case 13: print "\\r":;
                 return;
        case 27: print "\\e":;
                 return;
        case 34: print "\\\"":;
                 return;
        case 39: print "\\\'":;
                 return;
        case 92: print "\\\\":;
                 return;
    }
    if (a > 31 && a < 127) {
        print char(a):;
        return;
    } 

3. Nepočítaná smyčka typu while

Základním typem smyčky v prakticky všech programovacích jazycích podporujících strukturované programování, je smyčka typu while. Tato smyčka je postupně vykonávána tak dlouho, dokud platí zadaná podmínka, která je při každém průchodu smyčkou (tj. při každé iteraci) znovu vyhodnocena. Slovem „platí“ je zde myšlen stav, kdy se hodnota výrazu smyčky vyhodnotí na pravdivostní hodnotu true. V případě, že se hodnota výrazu smyčky vyhodnotí na pravdivostní hodnotu false, tělo smyčky se nebude dále provádět. Tento typ nepočítané smyčky je univerzální, všechny další typy smyček je možné převést na smyčku typu while, i když to v některých případech může být komplikované. Smyčka typu while se v calcu zapisuje následujícím způsobem:

while (výraz) příkaz 

Ve výše uvedeném zápisu představuje výraz podmínku smyčky a příkaz tělo smyčky. Pokud je zapotřebí tělo smyčky rozšířit na více příkazů, musí se opět použít příkazový blok, takže se zápis celé smyčky změní následujícím způsobem:

while (výraz) {
    příkaz1;
    příkaz2;
    příkaz3;
    ...
} 

Po spuštění následujícího skriptu se bude v nekonečné smyčce vypisovat řetězec „Hello world!“. Běh skriptu je možné ukončit například klávesovou zkratkou [Ctrl+C]:

while(1) print "Hello world!"; 

Další skript po svém spuštění vypíše prvních deset mocnin dvojky:

i=0;
while (i<10) {print 2^i; i++}
1
2
4
8
16
32
64
128
256
512 

Smyčku typu while (ale i další dva typy smyček) je možné ukončit i přímo z těla smyčky. K tomu slouží příkaz (resp. klíčové slovo) break, po jehož provedení se smyčka ihned ukončí bez návaznosti na hodnotu podmínky a běh programu pokračuje prvním příkazem za smyčkou. Toto chování demonstruje další příklad, ve kterém je smyčka pozastavena v případě, že hodnota proměnné i dosáhne hodnoty 10. Všimněte si, že bez použití příkazu break by se jednalo o nekonečnou smyčku, protože výraz řídicí iterace se vždy vyhodnotí na pravdivostní hodnotu true.

i=0;
while (1) {print i; if (i==10) break; i++}
0
1
2
3
4
5
6
7
8
9
10 

Posledním příkazem, který ovlivňuje běh smyčky, je příkaz continue. Pokud interpreter dojde až k tomuto příkazu, je běh skriptu ihned přenesen na začátek smyčky, tj. v závislosti na umístění příkazu continue může dojít k přeskočení části těla smyčky. Tento příkaz je možné použít i u dalších dvou typů smyček, jeho význam je stále stejný.

4. Nepočítaná smyčka typu do-while

Dalším typem nepočítané smyčky je smyčka typu do-while. Od výše zmíněné smyčky typu while se tento typ smyčky odlišuje v tom, že podmínka se testuje až na konci smyčky, tj. nejdříve po prvním průchodu smyčkou – je tedy zaručeno, že tělo smyčky se provede minimálně jedenkrát, zatímco u smyčky typu while se při nesplnění podmínky nemusí tělo smyčky provést ani jednou. Smyčka do-while díky tomu už není tak univerzální a také je méně přehledná, zejména při přechodu z programovacích jazyků, které používají smyčky typu repeat-until, u kterých je podmínka na konci smyčky de facto negována. Na druhou stranu se při programování smyčka do-while někdy využívá a to zejména v případech, kdy by použití smyčky typu while vedlo k duplikaci části kódu před smyčkou a v těle smyčky. Syntaxe smyčky do-while je následující:

do příkaz while (výraz) 

Popř. při požadavku na více příkazů uvnitř těla smyčky:

do {
    příkaz1;
    příkaz2;
    příkaz3;
    ...
} while (výraz); 

V předchozí kapitole jsme si uvedli skript sloužící pro výpočet a zobrazení prvních deseti mocnin dvojky. Tento skript je možné jednoduše přepsat s využitím smyčky typu do-while:

i=0;
do {print 2^i; i++} while (i<10) 
1
2
4
8
16
32
64
128
256
512 

Rozdíl mezi smyčkami while a do-while si ukážeme na situaci, kdy je změněna hodnota proměnné i, která slouží k řízení smyčky. V prvním případě není tělo smyčky provedeno ani jednou, v případě druhém k jedné iteraci dojde:

i=10;
while (i<10) {print 2^i; i++}   
(nic se nevypíše, smyčka neproběhne ani jednou)
i=10;
do {print 2^i; i++} while (i<10)
1024 

5. Počítaná smyčka typu for

Třetím a posledním typem smyčky, kterou je možné ve skriptech psaných pro calc použít, je počítaná smyčka typu for, jejíž syntaxe opět vychází z programovacího jazyka C, jak je to ostatně patrné z následující šablony:

for (výraz1; výraz2; výraz3) příkaz 

Popř. při požadavku na více příkazů uvnitř těla smyčky:

for (výraz1; výraz2; výraz3) {
    příkaz1;
    příkaz2;
    příkaz3;
    ...
} 

Při provádění této smyčky je nejdříve vyhodnocen výraz1, a to ještě před prvním opakováním příkazu či příkazů uvedených ve smyčce. V tomto výrazu je typicky nastavena hodnota řídicí proměnné smyčky, tj. proměnné, která se postupnými iteracemi zvyšuje či snižuje (není to však nutné, tento typ smyčky je možné zapsat i bez použití řídicích proměnných). Výraz výraz2 je vyhodnocen před každým průchodem tělem smyčky. Hodnota tohoto výrazu řídí počet opakování: pokud je výraz2 vyhodnocen na nenulovou hodnotu, je smyčka (tj. příkaz či příkazy uvedené v těle smyčky) provedena, pokud je naopak vyhodnocená hodnota výrazu výraz2 nulová, je řízení běhu skriptu převedeno na příkaz, který se nachází za smyčkou. Výraz výraz3 je vyhodnocen po každém průchodu smyčkou – z tohoto důvodu je při typickém použití třetí výraz určen pro změnu hodnoty řídicí proměnné smyčky.

Následuje příklad ilustrující použití počítané smyčky typu for. V tomto příkladu je použita definice funkce factorial(), která je uvedena v další kapitole:

for (i=0; i<20; i++) print factorial(i);
1
1
2
6
24
120
720
5040
40320
362880
3628800
39916800
479001600
6227020800
87178291200
1307674368000
20922789888000
355687428096000
6402373705728000
121645100408832000 

U příkazu print je možné použít několik argumentů, které mohou sloužit například pro vytvoření jednoduché (neformátované) tabulky:

for (i=0; i<20; i++) print i,'! =', factorial(i);
0 ! = 1
1 ! = 1
2 ! = 2
3 ! = 6
4 ! = 24
5 ! = 120
6 ! = 720
7 ! = 5040
8 ! = 40320
9 ! = 362880
10 ! = 3628800
11 ! = 39916800
12 ! = 479001600
13 ! = 6227020800
14 ! = 87178291200
15 ! = 1307674368000
16 ! = 20922789888000
17 ! = 355687428096000
18 ! = 6402373705728000
19 ! = 121645100408832000 

Programátoři znalí programovacího jazyka C mohou i v calcu použít funkci printf() sloužící k formátovanému výstupu:

for (i=0; i<20; i++) printf("%d! = %d\n", i, factorial(i));
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 355687428096000
18! = 6402373705728000
19! = 121645100408832000 

Příkaz print a funkce printf() budou vysvětleny v navazující části tohoto seriálu.

6. Vytváření nových funkcí

Při tvorbě složitějších skriptů je vhodné celý výpočet rozdělit do více funkcí. Skript se na jednu stranu stane přehlednějším, protože se ve funkcích skryjí implementační detaily, na stranu druhou se mohou jednodušší funkce snadno otestovat a popř. znovupoužít při dalších výpočtech. Nové funkce se v calcu vytváří pomocí klíčového slova define za nímž následuje jméno nově definované funkce. Ihned za jménem se nachází kulaté závorky se seznamem parametrů bez udání typu a za parametry je uvedeno tělo funkce uzavřené do složených závorek. Pokud má funkce vracet nějakou hodnotu, je nutné v jejím těle použít (i na několika místech) příkaz return za nímž následuje výraz, který se vyčíslí a jeho výsledek se použije jako návratová hodnota. Podobně jako u vstupních parametrů funkce, i u výstupního výrazu se nemusí specifikovat datový typ, ten se vyhodnocuje až za běhu skriptu. Definovanou funkci je možné ze slovníku vymazat příkazem undefine jméno_funkce, tento příkaz však není možné použít pro funkce interní.

V následujícím příkladu je ukázána definice funkce soucet a následně i její volání:

define soucet(a,b) {
    return a+b;
}

soucet(a,b) defined
print soucet(10,20)
30 

Funkce může ve svém těle volat sebe samu, takže je možné naprogramovat i rekurzi. V dnešním posledním příkladu ukázána tvorba rekurzivní podoby faktoriálu. Faktoriál je samozřejmě možné naprogramovat i pomocí počítané smyčky, jeho rekurzivní podoba je však často použita právě pro ilustraci programové rekurze:

CS24_early

define factorial(n) {
    if (n<0) {
        print "Faktoriál nelze pro záporná čísla vypočítat";
        return;
    }
    else if (n==0) return 1;
    else return n*factorial(n-1);
} 

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

V dalším pokračování tohoto seriálu dokončíme popis aplikace calc. Budeme se věnovat pokročilejším technikám, například práci s maticemi či objekty.

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.