Programovanie‎ > ‎

Štruktúry

V doterajšom výklade sme sa dozvedeli, že sa program skladá z funkcie setup() a loop() a z príkazov, ktoré tieto funkcie obsahujú. Svoje intuitívne predstavy o programe rozšírime nielen o prípadné ďalšie funkcie, ale detailnejšie sa pozrieme aj na obsah funkcií. Telo funkcie obsahuje rad príkazov. Naším cieľom je vykonávať práve tie príkazy, ktoré odpovedajú zvolenému zámeru. Výber z príkazov je určený stavom doterajšieho behu programu, vstupnými údajmi a riadiacimi štruktúrami, ktoré sme použili. Program, vykonávajúci príkazy v pevnom a nemennom poradí, ktorý zodpovedá ich umiestnení v zdrojovom texte a naviac bez možnosti ich výberu, iste nieje naším ideálom. A to bez ohľadu na zvolený vyšší programovací jazyk. Cieľom tejto kapitoly je preto zoznámenie sa s riadiacimi štruktúrami.


if

Ak chceme, aby sa určitá časť kódu vykonala len v určitých prípadoch, prichádzajú na rad podmienky. Bez podmienok sa prakticky nezaobídete. Proste sa stále dostávate do situácií, kedy budete potrebovať, aby sa jedna časť vykonala len keď niečo platí. Podmienka sa rieši pomocou príkazu if (anglicky ak).

Príkaz if testuje, či bola dosiahnutá určitá podmienka, napr. či analógová hodnota je väčšia ako zadaná a vykonáva všetky príkazy vnútri zložených zátvoriek, ak tvrdenie je pravdivé (TRUE). Ak tvrdenie pravdivé nie je (FALSE), program príkazy vnútri zložených zátvoriek preskočí.

Formát príkazu if je:

if (premenná ?? hodnota)   // podmienkový výraz
{
  príkazy;
}

Všimnime si okrúhlych zátvoriek okolo podmienkového výrazu. Tie sú potrebné a nie je možné ich vynechať, ukončujú totiž výraz. Podmienkový výraz sa skladá z relačných operátorov alebo z logických operátorov, prípadne ich kombináciou. Tu je dôležité dbať na to, že ak spájame podmienky, tak je lepšie, keď je každá časť v zátvorke, inak to robí neplechu.

if ((znak >= 'a') && (znak <= 'z'))

V podmienkovom výraze môžete použiť aj návratovú hodnotu TRUE alebo FALSE nejakej funkcie napr:

if (!Serial.available())

Jazyk C vykonáva v základe tzv. skrátené vyhodnocovanie výrazov. V praxi to znamená, že podmienka je vyhodnocovaná zľava doprava a akonáhle je jasné, aké hodnoty podmienka nadobudne, vyhodnocovanie je ukončené.

if ((a == 1) && longCalculation())

V kóde je uvedený zložený podmienkový výraz. Ak je hodnota premennej a iná ako 1, k zavolaniu funkcie longCalculation() vôbec nedôjde. To sa môže hodiť, ak vieme, že takáto funkcia beží dlho.

V jazyku Wiring je možné použiť niekoľko spôsobov zápisov.

if (x > 120) digitalWrite(LEDpin, HIGH); 

if (x > 120)
digitalWrite(LEDpin, HIGH); 

if (x > 120){ digitalWrite(LEDpin, HIGH); } 

if (x > 120){ 
  digitalWrite(LEDpin1, HIGH);
  digitalWrite(LEDpin2, HIGH); 
} 

POZOR na omylom použité jednoduché rovná sa (napr. if (x = 10)). Jedno znamienko rovnosti je operátor priradenia a to vloží hodnotu 10 do premennej x a výsledkom bude PRAVDA. Pri použití znamienka dvojité rovná sa (napr. v prípade (x == 10)) sa porovnáva, či x je rovné 10 alebo nie (= priradenie; == porovnanie).

Je to preto, že program vyhodnotí príkaz if (x = 10) nasledovne: 10 je priradené do x, takže teraz obsahuje x hodnotu 10. Potom podmienka if vyhodnotí 10, čo je vždy vyhodnotené ako PRAVDA, pretože akékoľvek nenulové číslo je vyhodnotené ako PRAVDA. V dôsledku toho sa podmienka if (x = 10) vždy vyhodnotí ako PRAVDA, čo nie je požadovaný výsledok. Okrem toho, premenná x má teraz priradenú hodnotu 10, čo tiež nie je požadovaný výsledok.


if... else

Operácia "if... else" umožňuje rozhodovanie štýlom "buď - alebo" a vetvenie programu podľa výsledku operácie. Napríklad, ak chcete otestovať stav digitálneho vstupu a potom vykonať nejakú činnosť, ak je vstupný pin v stave HIGH alebo naopak vykonať niečo iného, ak je vstupná úroveň v stave LOW, môžete to zapísať týmto spôsobom:

if (inputPin == HIGH)
{
  // činnosť 1
}
else
{
  // činnosť 2
}

Alebo môže tiež zadať ďalšie if, ak testujeme viac vzájomne sa vylučujúcich podmienok. Testy možno spustiť súčasne. Je dokonca možné mať neobmedzený počet týchto vetvení else. Pamätajte si však, že je možné vždy spustiť v závislosti od podmienok testov len jednu časť kódu:

if (inputPin < 500)
{
  // činnosť A
}
else if (inputPin >= 1000)
{
  // činnosť B
}
else
{
  // činnosť C
}

switch case

Switch je špeciálny druh podmienky. Špeciálny je v tom, že sa zaoberá iba premennou a jej hodnotou. Program prechádza každú vetvu konštrukcie switch a testuje hodnotu premennej. Ďalší rozdiel je v tom, že sa môže vykonať aj viac vetiev (čo u if sa nedá). Pokiaľ ale chceme, aby po vykonaní vetiev program pokračoval až za koncom konštrukcie switch, musíme použiť na konci vetvy príkaz break. Syntakticky zapísaný príkaz switch má tento tvar:

switch (<podmienkový výraz>)
{
  case <hodnota>:<príkaz>
  ...
  <[default:<príkaz>]>
}

Podmienkový výraz sa musí vyhodnotiť ako celočíselná hodnota (teda i znak). Porovnávanie iných typov nie je možné. Príkaz funguje tak, že sa najprv vyhodnotí výraz a jeho výsledok sa potom hľadá odhora v hodnotách jednotlivých case vetiev (nesmú sa používať dve vetvy s rovnakou hodnotou). Akonáhle sa nájde prvá zhoda, vykoná sa príkaz za dvojbodkou. Na rozdiel od Pascalu sa potom pokračuje vo vykonávaní nasledujúcich vetiev, interne sa totiž pri zhode hodnôt iba skočí na dané miesto v kóde a všetko od tohto miesta sa vykoná. Ak chceme vykonať práve jednu vetvu (čo je obvyklé), je nutné príkaz na konci vetvy ukončiť príkazom break. Ak sa nenájde zhoda so žiadnou hodnotou, vykoná sa nepovinná vetva default.

int i = 1;
switch (i)
{
  case 2:
    i = 4;
    break; // ukončenie vetvy
  case 3:
    i = 6;
    // tu má byť break, inak sa vždy vykoná časť default
  default:
    i = 0;
}

V tomto príkaze switch sa rozhodujeme na základe hodnoty premennej i. Ak je hodnota dve, nastavíme premennú na hodnotu štyri a príkaz switch ukončíme. Ak je hodnota tri, nastavíme premennú na hodnotu šesť a ďalej znovu nastavíme hodnotu na nulu, pretože sme neukončili príkaz a ten tak bude pokračovať vo vykonávaní zvyšného kódu.


for

Cyklus je časť programu, ktorý je v závislosti na podmienke vykonávaný opakovane. U cyklu obvykle rozlišujeme riadiacu podmienku cyklu a telo cyklu. Riadiaca podmienka cyklu určuje, či bude vykonané telo cyklu alebo bude riadenie predané za príkaz cyklu. Telo cyklu je príkaz, spravidla v podobe bloku. Cykly môžeme rozdeliť podľa toho, či sa telo vykoná aspoň jedenkrát a cykly, kde telo nemusí byť vykonané vôbec. Rozhodne môžeme povedať, že aj keď v C existujú rôzne typy cyklov, je možné vystačiť si s jedným z nich. V tejto podkapitole si povieme o všetkých. Ich výber ponecháme na vhodnosti použitia v danej situácii a aj na ich individuálnej obľúbenosti. Ako prvý si predstavíme cyklus for.

Príkaz for sa používa k niekoľkonásobnému opakovaniu bloku príkazov, uzatvorených do zložených zátvoriek. Záhlavie slučky sa skladá z troch častí, oddelených bodkočiarkou (;):

for ( [<expr1>] ; [<expr2>] ; [<expr3>] ) <statement>

Nepovinný výraz expr1 je vykonaný pred prvým vyhodnotením testu. Typicky sa používa pre inicializáciu premenných pred cyklom. Po každom prevedení tela cyklu statement, prevedie program nepovinný výraz expr3. Typicky sa jedná o prípravu ďalšej iterácie cyklu. Pokiaľ neuvedieme testovací výraz expr2, použije prekladač hodnotu 1 a teda bude prevádzať nekonečný cyklus. Našťastie môžeme použiť príkaz break a continue.

Klasická forma cyklu for vyzerá takto:

for (inicializácia; podmienka; výraz)  
{
  príkazy;
}

V cykle for najskôr vytvoríme premennú (inicializácia). Potom za bodkočiarkou napíšeme podmienku, ktorá musí platiť, aby sa cyklus vykonal. Za poslednou bodkočiarkou potom príde akcia (výraz), ktorá sa po každom vykonaní s premennou urobí. Ak by sme napríklad chceli vypísať čísla od nuly do deviatich, môžeme to urobiť takto:

for (int i = 0; i < 10; i++) {
  Serial.println(i);
}

Najskôr sme si vytvorili premennú int i aj s hodnotou nula. Podmienka je, že i bude menšie ako desať. Pokiaľ bude presne desať alebo viac (k čomu tu nemôže dôjsť), tak sa cyklus prestane vykonávať a kód prejde ďalej. Posledná je akcia, ktorá sa s premennou vykoná, teda sa k nej pripočíta jedna. Cyklus sa teda vykoná, keď je v i nula, pripočíta sa 1, i je jedna, znovu sa vykoná a tak ďalej.

Cyklom for možno ale tiež simulovať správanie cyklu while jednoduchým vynechaním prvej a poslednej časti:

int i;
// ...
for (;i != 10;)
{
  //...
}

kde cyklus pobeží, dokiaľ bude premenná i rôzna od desať.


while

Všetky príkazy v cykle sa vykonávajú, pokiaľ je podmienka pravdivá. Za zmienku stojí, že program kontroluje platnosť podmienky vždy na začiatku cyklu, ak je nepravdivá, cyklus skončí. Vyjadrené slovami: "Ak je podmienka pravdivá, urob toto a vráť sa na začiatok. Ak nie je, skonči ". Cyklus while () sa teda nemusí vykonať vôbec. Používa sa nasledujúce syntaxe:

while ( <expression> ) <statement>

Príkaz while, vykonáva príkaz statement (telo cyklu) viackrát alebo vôbec, pokiaľ má v danom kontexte testovací výraz expression nenulovou hodnotu. Príkaz je veľmi často blokom. Tu sa najmä začiatočníci mylne domnievajú, že pokiaľ sa kontext kdekoľvek behom prevádzania príkazov bloku tela cyklu zmení a podmienka tak prestane byť splnená, je cyklus ukončený. To však nie je pravda. Pravidlo hovorí, že sa príkaz statement prevedie, ak je podmienka expression splnená. Ak je príkazom blok, prevedie sa teda blok celý. Až potom sa znovu prevedie test.

Klasická forma cyklu while vyzerá takto:

while (<podmienkový výraz>)
{
  <príkazy>;
}

While znamená kým (pokiaľ). Ten sa hodí, ak treba chceme, aby pri stlačení tlačidla začala blikať LEDka a neprestala kým tlačidlo neuvoľníme.

while(digitalRead(10)){
  // blikanie
}

Čo ale keby sme chceli, aby sa cyklus spustil pri štarte Arduina a neukončil sa, kým nestlačíme jedno tlačidlo alebo druhé tlačidlo? Išlo by to nejako skombinovať do podmienky while, ale ak by pribudlo tretie tlačidlo, potom štvrté ... Predsa musí existovať lepší spôsob, ako slučku ukončiť. Takže dajme tomu, že pri spustení Arduina sa začne vykonávať cyklus, ktorý bude niečo robiť a ukončí sa až pri stlačení jedného, druhého, tretieho alebo štvrtého tlačidla bez neprehľadného zápisu v podmienke. Ako? Pomocou príkazu break, ktorý cyklus ukončí.

while(true)     // nekonečná podmienka, ukončí se len príkazom break,
{               // pokiaľ k tomu nemáte vážny dôvod, nepoužívajte ju
        ...
        if(digitalRead(10))
         break;
        if(digitalRead(11))
         break;
        ...
}

do... while

Cykly while a do...while sú si veľmi podobné a líšia sa len prvým priebehom. U while sa podmienkový výraz vyhodnotí pred prvým prevedením tela cyklu, zatiaľ čo u do...while sa telo prevedie vždy aspoň raz. Inak povedané, jeho testovací príkaz statement je testovaný až po priechode telom cyklu. Pokiaľ je test splnený, prevádza sa znovu telo cyklu. Po syntaktickej stránke tvorí telo cyklu opäť výraz expression:

do <statement> while ( <expression> );

Klasická forma cyklu do...while vyzerá takto:

do 
{  
  <príkazy>;
} while (<podmienkový výraz>);

Príklad počítania od 0 do 99:

int x = 0;
do {
 Serial.println(x);
 x++;
} while(x < 100);

Časté použitie príkazu do...while nájdeme napríklad v tých numerických výpočtoch, kedy je aspoň jedna iterácia nevyhnutná.


break a continue

V jazyku C existujú dva príkazy pre ovplyvnenie vykonávanie cyklov. Je to break a continue. Príkaz break ukončí okamžite vykonávanie cyklu a program nasleduje prvým príkazom za cyklom, continue okamžite ukončí aktuálnu iteráciu a začne novú. Oba príkazy je možné volať iba vo vnútri tela cyklu (príkaz break možno umiestniť aj do príkazu switch).


goto

Príkaz skoku patrí k zatracovaným príkazom štruktúrovaného programovania. Skutočne s jeho pomocou dokáže menej skúsený programátor vytvoriť zdrojový text, pre ktorý sa vžilo označenie špagetový. Skutočnosť, že sme si príkaz skoku nezatajili má tri príčiny:

1. Jazyk C proste príkaz skoku obsahuje. Jeho neuvedenie by nebolo fér.

2. Jazyk C umožňuje ako systémový prostriedok písať programy na veľmi nízkej úrovni. Často veľmi blízke strojovému kódu. Použitie skoku v takom prípade môže byť veľmi žiaduce.

3. Skokové príkazy break, continue a return sú označované ako štruktúrované skoky. Ich použitie teórie (ani praxe) programovanie neodmieta.

Zostáva nám teda len príkaz skoku goto. Jeho syntax je jednoduchá:

identifier: <statement> ;

goto <identifier> ;

Uviedli sme si nielen syntax goto samotného, ale aj súvisiaceho návestie identifier. Návestie neprevádza žiadnu akciu a nemá iný vplyv na riadenie behu. Jeho identifikátor len označuje nejaké miesto v zdrojovom texte. Príkaz goto prenáša riadenie na príkaz, označený návestím. V rámci jednej funkcie nesmú byť dve rovnaké návestia.


return

Pokiaľ má funkcia niečo vracať, musí mať iný dátový typ ako void. Pre vrátenie vybranej hodnoty sa používa príkaz return. Ak chceme vrátiť reťazec znakov, nepoužíva sa pole char[], ale dátový typ String. Tiež nie je možné jednoduchým spôsobom vrátiť pole. Ostatné dátové typy sa používajú rovnako.

Jednoduchá funkcia pre sčítanie dvoch čísel a vrátenie súčtu potom vyzerá takto:

void setup() { 
  Serial.begin(9600);
  Serial.println(scitaj(10,11)); 
} 

void loop(){ 
}

int scitaj(int a, int b){ 
  int sucet = a + b; 
  return sucet; 
}