<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Karel</maintitle>
  <author>Jiří Znamenáček</author>
  <title>Cykly (smyčky)</title>
  <date>2025-09-16</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
</meta>
<!--
  „“–
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->


<slide title="Úvod">

    <p>
        Při programování se často stane, že nějaký kus kódu použijeme více než jednou. V principu jsou možné dvě varianty:
    </p>
    <ul>
        <li>
            <em>Nějaký kus kódu potřebujeme na více různých místech programu.</em>
            – Pak se vyplatí daný kus kódu „proměnit“ v nový příkaz pro Karla a na příslušných místech programu ho volat.
        </li>
        <li>
            <em>Část kódu v jistém místě programu potřebujeme v tom samém místě vícekrát zopakovat.</em>
            – Uvedený kód zabalíme do tzv. <strong>smyčky</strong> (též <em>cyklu</em>), která určuje, kolikrát se daná část kódu programu zopakuje.
        </li>
    </ul>
    <p>
        V dalším se podíváme, jak se v Karlovi řeší právě cykly/smyčky.
    </p>

</slide>
<slide title="Motivace">

  <p>
    <code>ČELEM VZAD</code> i první varianta <code>VPRAVO VBOK</code> z <a href="overview.xml?slajd=3">úvodní přednášky</a> se vyznačují tím, že stejnou část kódu (v tomto případě tedy pouze jeden příkaz <code>VLEVO VBOK</code>) voláme v tom samém místě programu více než jednou:
  </p>
  <example lang="karel">
    ČELEM VZAD
      VLEVO VBOK
      VLEVO VBOK
    KONEC

    VPRAVO VBOK
      VLEVO VBOK
      VLEVO VBOK
      VLEVO VBOK
    KONEC
  </example>
  <p>
    Jelikož opisovat větší části kódu ručně je nejen otravné, ale i nebezpečné (snadno se tím do programu zanášení úplně zbytečné chyby), obsahuje většina programovacích jazyků pro opakované volání části kódu speciální konstrukce. A Karel v tomto není výjimkou.
  </p>

</slide>
<slide title="Cykly aneb smyčky">

  <p>
    Vícenásobné opakování části kódu v Karlovi zajišťuje příkaz <code>OPAKUJ</code>:
  </p>
  <example lang="karel">
    OPAKUJ x-KRÁT
      známý příkaz
      známý příkaz
      ...
    KONEC
  </example>
  <p>
    ..kde místo <strong>x</strong> dosadíme číslo vyjadřující požadovaný počet opakování. Například <code>VPRAVO VBOK</code> bychom tak mohli zapsat následujícím způsobem:
  </p>
  <example lang="karel">
    VPRAVO VBOK
      OPAKUJ 3-KRÁT
        VLEVO VBOK
      KONEC
    KONEC
  </example>
  <p>
    Pravda, na počtu řádek kódu jsme vůbec neušetřili (a řešit takto <code>ČELEM VZAD</code> by bylo ještě podivnější – zkuste si to) a smyčkou jsme navíc zatáhli do programu novou konstrukci, se kterou si musí Karel poradit. Ale zato jsme explicitně uvedli, že jeden konrétní příkaz <code>VLEVO VBOK</code> voláme třikrát za sebou, což není k zahození.
  </p>
  <p>
    PS: Pokud by se měla opakovat větší část kódu, budeme za smyčky nakonec moc rádi.
  </p>

</slide>
<slide title="Nekonečná smyčka">

  <p>
    Poznamenejme ještě, že u <code>OPAKUJ</code> je možno úplně vynechat počet opakování, čímž vyrobíme <em>nekonečnou smyčku</em>:
  </p>
  <example lang="karel">
    VLEVOTOČ
      OPAKUJ
        VLEVO VBOK
      KONEC
    KONEC
  </example>
  <p>
    Takovouto smyčku buď musíme ukončit ručně z GUI nebo v sobě musí nějakým způsobem obsahovat ukončení (ať už přímo vyvoláním příkazu <code>STOP</code> při splnění nějaké ukončovací podmínky nebo nepřímo zavoláním dalšího příkazu, který zařídí totéž).
  </p>

</slide>
<slide title="Opakování s podmínkou na začátku">

    <p>
        Kromě počtu opakování je možno cyklus řídit též podmínkou. To zařídí příkaz <code>DOKUD</code>:
    </p>
    <example lang="karel">
        DOKUD podmínka
          známé příkazy
        KONEC
    </example>
    <p>
        Karel bude opakovat <em>známé příkazy</em> tak dlouho, dokud bude <em>podmínka</em> platit. Jakmile platit přestane, bude Karel pokračovat ve zpracovávání programu za klíčovým slovem <code>KONEC</code>.
    </p>
    <p>
        Zatímco v cyklu <code>OPAKUJ</code> je počet opakování programátorovi dopředu přesně znám, u cyklu <code>DOKUD</code> řídí počet opakování některá z osmi podmínek. V limitním případě dokonce nemusí být podmínka splněna hned na začátku a tělo příkazu <code>DOKUD</code> se pak neprovede ani jednou.
    </p>

</slide>
<slide title="Ukázka – nalezení zdi">

    <p>
        Následující příkaz <code>KE ZDI</code> dovede Karla bezpečně k nejbližší zdi – Karel prostě bude dělat <code>KROK</code> tak dlouho, dokud se nedostane na pole před zdí:
    </p>
    <example lang="karel">
        KE ZDI
          DOKUD NENÍ ZEĎ
            KROK
          KONEC
        KONEC
    </example>
    <p>
        Všimněte si, že Karel na konci cesty nenabourá, ale spořádaně ukončí program – právě díky podmínce v cyklu. A pokud by na začátku vykonávání programu už stál před zdí, ani by se žádný krok udělat nepokoušel – tělo cyklu by se vůbec nevyvolalo, protože podmínka by byla neplatná.
    </p>
    <p>
        Podstatné je, že chůzi Karla nyní řídí podmínka – nemusíme vědět, jak daleko od zdi Karel stojí, stejně vždy dojde až k ní.
    </p>

</slide>
<slide title="Ukázka – sesbírání značek">

  <p>
    Za pomoci podmínky také pole velmi snadno zbavíme všech značek, aniž bychom potřebovali vědět, kolik jich na políčku leží (v kterémžto případě bychom si vystačili s příkazem <code>OPAKUJ</code>):
  </p>
  <example lang="karel">
    VYBER
      DOKUD JE ZNAČKA
        ZVEDNI
      KONEC
    KONEC
  </example>

</slide>
<slide title="Ukázka – položení značek I">

  <p>
    Velmi podobně také pole zaplníme značkami až po okraj, aniž by nás trápilo počítání, kolik značek můžeme přidat:
  </p>
  <example lang="karel">
    VYPLŇ
      DOKUD JE MÍSTO
        POLOŽ
      KONEC
    KONEC
  </example>
  <p>
    I v případě, že by políčko již bylo zcela zaplněné, příkaz se provede správně – podmínka <code>JE MÍSTO</code> by totiž v takovém případě byla nepravdivá hned na začátku, takže příkaz <code>POLOŽ</code> by se vůbec nezavolal.
  </p>

</slide>
<slide title="Ukázka – položení značek II">

    <p>
      Jak jsem o pár stran zpátky brojil proti komplikování příkazů zbytečným kódem, tak nyní Vám jeden takový ukážu:
    </p>
    <example lang="karel">
        VYPLŇ
          VYBER
          OPAKUJ 8-KRÁT
            POLOŽ
          KONEC
        KONEC
    </example>
    <p>
      Jelikož víme, že zaplnění prázdného políčka zabere právě osm značek, můžeme při znalosti příkazu <code>VYBER</code> nadefinovat příkaz <code>VYPLŇ</code> také takto – prostě nejdřív políčko zbavíme všech značek, abychom na něj požadovaných osm značek mohli bezpečně položit.
    </p>
    <note>
      A naopak při znalosti příkazu <code>VYPLŇ</code> bychom mohli podobně nadefinovat příkaz <code>VYBER</code>. Zkuste si to.
    </note>
    <p>
      Ve většině případů se takovémuto kódu budete chtít vyhnout. Ale možná se Vám někdy tenhle trošku „pokřivený“ způsob uvažování bude hodit.
    </p>

</slide>
<slide title="Ukázka – otočení severním směrem">

  <p>
    Jako poslední si ukažme příkaz, který natočí Karla na sever:
  </p>
  <example lang="karel">
    NA SEVER
      DOKUD NENÍ SEVER
        VLEVO VBOK
      KONEC
    KONEC
  </example>
  <p>
    Princip je opět jednoduchý – jelikož máme k dispozici podmínku, která nám ohlásí, zda je Karel otočený na sever nebo není, budeme se Karlem jednoduše otáčet <code>VLEVO VBOK</code> tak dlouho, dokud se na ten sever nedotočíme. Pokud už Karel na sever byl otočený na začátku příkazu, nestane se nic – nesplněná podmínka předá řízení rovnou za příslušný <code>KONEC</code>.
  </p>
  <note>
    Pochopitelně byste se mohli točit také pomocí <code>VPRAVO VBOK</code>, ale to by bylo dosti neekonomické – na každé jedno <code>VPRAVO VBOK</code> by Karel vykonal tři <code>VLEVO VBOK</code>.
  </note>
  <p>
    PS: Natočení Karla na zbývající tři světové strany si už dopište sami – je to úplně stejné, jen podmínky se budou lišit. Nezapomeňte si příkazy uložit pro pozdější použití, budou se nám hodit.
  </p>

</slide>
<!--slide title="Varianty opakování s podmínkou (i) na konci">

    <p>
        Řídící příkazy <code>DOKUD</code> a <code>OPAKUJ</code> mohou být na konci doplněny podmínkou <code>NEŽ</code>, resp. <code>AŽ</code>:
    </p>
    <examples>
        <example lang="karel">
      OPAKUJ počet-KRÁT
        známé příkazy
      NEŽ podmínka

      OPAKUJ počet-KRÁT
        známé příkazy
      AŽ podmínka
        </example>
        <example lang="karel">
      DOKUD podmínka 1
        známé příkazy
      NEŽ podmínka 2

      DOKUD podmínka 1
        známé příkazy
      AŽ podmínka 2
        </example>
    </examples>
    <note>
        Místo klíčového slovíčka <code>NEŽ</code> můžete použít i slovíčko <code>AŽ</code>, ale možná vám – stejně jako mně – zní (nejen) v tomto kontextu divně.
    </note>
    <p>
        Podmínka za klíčovým slovem <code>NEŽ</code>, resp. <code>AŽ</code>, je vyhodnocována vždy po provedení <em>známých příkazů</em> těla příkazu (samozřejmě pouze tehdy, dostane-li se program dovnitř těla) a pokud <strong>je splněna</strong>, dojde k opuštění příkazu (a to dříve, než by totéž zajistily podmínky na začátku nebo došlo k havárii).
    </p>

</slide-->
<slide title="„OPAKUJ“ s podmínkou na konci">

    <p>
        Kromě podmínky na začátku cyklu z předchozích slajdů (příkaz <code>DOKUD</code>) rozumí Karel i podmínce na konci cyklu. Vše zastane již známý příkaz <code>OPAKUJ</code>, pouze místo slova <code>KONEC</code> je na jeho konci podmínka:
    </p>
    <examples>
        <example lang="karel">
            OPAKUJ
              známé příkazy
            NEŽ podmínka
        </example>
        <example lang="karel">
            OPAKUJ
              známé příkazy
            AŽ podmínka
        </example>
    </examples>
    <note>
        PS: Místo klíčového slovíčka <code>NEŽ</code> můžete použít i slovíčko <code>AŽ</code> – každé z nich zní v různých situacích různě divně, takže si vyberte ^_~
    </note>
    <p>
        Všimněte si jednoho veledůležitého rozdílu proti <a href="?slajd=5">předchozímu případu</a> – v tomto případě se <strong>příkazy v těle cyklu provedou přinejmenším jednou</strong>! Stojí-li tudíž Karel před vykonáním programu již před zdí, chtě nechtě do ní nabourá.
    </p>
    <note>
        U <code>DOKUD podmínka</code> je totiž podmínka vyhodnocena jako první a teprve poté se tělo cyklu buď provede, nebo neprovede. Ale u cyklu <code>OPAKUJ … NEŽ podmínka</code> se tělo cyklu provede přinejmenším jednou, než bude poprvé vyhodnocena podmínka, a podle výsledku jejího vyhodnocení se v cyklu bude buď pokračovat, nebo ne.
    </note>

</slide>
<slide title="Ukázka – zaplnění políčka značkami I">

  <p>
    Ukažme si příklad vyplnění políčka značkami za použití podmínky <code>AŽ</code>:
  </p>
  <example lang="karel">
    VYPLŇ
      OPAKUJ 8-KRÁT
        POLOŽ
      AŽ NENÍ MÍSTO
    KONEC
  </example>
  <p>
    Zde si můžeme dovolit natvrdo napsat osminásobné opakování příkazu <code>POLOŽ</code>, protože podmínka <code>AŽ NENÍ MÍSTO</code> zajistí případné předčasné ukončení cyklu dříve, než by program zhavaroval na pokusu položení značky do zcela zaplněného pole. (Přítomnost osmi značek na poli totiž splní podmínku <code>NENÍ MÍSTO</code>, díky čemuž <code>AŽ</code> ukončí cyklus.)
  </p>

</slide>
<slide title="Ukázka – zaplnění políčka značkami II">

    <p>
        Tento ukázkový program však obsahuje chybu, která přímo souvisí s tím, že podmínka se vyhodnocuje až na konci – selže totiž, pokud ho zavoláte na místě, které už je plně obsazené značkami!
    </p>
    <p>
        Podmínka <code>NEŽ</code> se totiž poprvé vyhodnocuje <em>až po prvním provedení těla příkazu</em>, takže Karel se pokusí jednou položit značku a selže při tom. Správněji (a méně přehledně) by tedy program měl být napsán takto:
    </p>
    <example lang="karel">
        VYPLŇ
          KDYŽ JE MÍSTO
            OPAKUJ 8-KRÁT
              POLOŽ
            AŽ NENÍ MÍSTO
          KONEC
        KONEC
    </example>
    <p>
        Nejlepší je samozřejmě použít rovnou <a href="?slajd=8"><code>DOKUD JE MÍSTO</code></a> nebo <a href="recursion.xml?slajd=13">rekurzi</a>.
    </p>

</slide>
<slide title="Ukázka – nalezení zdi">

    <p>
        S úplně stejným problémem se potkáte, pokud se tímto způsobem pokusíte nalézt zeď:
    </p>
    <examples>
        <example lang="karel">
            KE ZDI
              OPAKUJ
                KROK
              NEŽ JE ZEĎ
            KONEC
        </example>
        <example lang="karel">
            KE ZDI
              KDYŽ NENÍ ZEĎ
                OPAKUJ
                  KROK
                NEŽ JE ZEĎ
              KONEC
            KONEC
        </example>
    </examples>
    <p>
        Pokud by Karel na začátku stál před zdí, ihned by do ní naboural, proto tento případ musíte opět pohlídat dodatečným příkazem <code>KDYŽ</code>. Anebo to samozřejmě rovnou napsat pomocí <a href="?slajd=6"><code>DOKUD NENÍ ZEĎ</code></a> nebo <a href="recursion.xml?slajd=14">rekurzí</a>.
    </p>

</slide>
<slide title="Opakování s podmínkou na začátku i na konci I">

    <p>
        Dilema předchozích příkladů – problém dodatečné podmínky na začátku – můžete ale vyřešit také díky tomu, že náš Karel (v zájmu menšího zmatení nepřítele :-) rozumí i variantám cyklů s podmínkami na obou koncích:
    </p>
    <examples>
        <example lang="karel">
            DOKUD podmínka 1
              příkazy
            AŽ podmínka 2
        </example>
        <example lang="karel">
            DOKUD podmínka 1
              příkazy
            NEŽ podmínka 2
        </example>
    </examples>
    <p>
        Můžete tedy hlídat zároveň jak vstup do cyklu, tak jeho opuštění na konci:
    </p>
    <example lang="karel">
        KE ZDI
          DOKUD NENÍ ZEĎ
            KROK
          NEŽ JE ZEĎ
        KONEC
    </example>
    <p>
        Rozumí se samo sebou, že obě podmínky mohou být i stejné, ať už to dává smysl nebo ne. A je otázka, zda (nejen) v tomto případě nám stojí obsluha vlastně stejné podmínky na začátku i na konci za značné znepřehlednění vyrobené procedury.
    </p>

</slide>
<slide title="Opakování s podmínkou na začátku i na konci II">

    <p>
        Ještě existuje varianta podmínky na konci v kombinaci s určeným počtem opakování cyklu, tedy příkazem <code>OPAKUJ</code>:
    </p>
    <examples>
        <example lang="karel">
            OPAKUJ n-krát
              příkazy
            AŽ podmínka
        </example>
        <example lang="karel">
            OPAKUJ n-krát
              příkazy
            NEŽ podmínka
        </example>
    </examples>
    <p>
        Použití této konstrukce je stejně jako u předchozí <a href="?slajd=15"><code>DOKUD podmínka 1 – NEŽ podmínka 2</code></a> poměrně specifické, ale někdy se hodit může.
    </p>

</slide>
<slide title="PS: Nekonečná smyčka díky nesplnitelné podmínce">

  <p>
    Příkaz <code>OPAKUJ</code> v kobinaci s testem <code>NEŽ</code> (resp. <code>AŽ</code>) můžete využít pro výrobu nekonečného cyklu – stačí navolit podmínku tak, aby při opakovaných příkazech těla cyklu nebyla nikdy splněna. Například následující program..
  </p>
  <example lang="karel">
TOČ SE
  OPAKUJ
    VLEVO VBOK
  NEŽ JE DOMOV
KONEC
  </example>
  <p>
    ..způsobí, že se Karel bude točit stále kolem dokola na jednom místě (samozřejmě pokud už nestál zrovna doma, v kterémžto případě by se otočil vlevo pouze jednou).
  </p>

</slide>
<slide title="Souvislost s rekurzí – přenesení značek">

    <p>
        Zopakovat část programu na základě nějaké podmínky zní velmi podobně rekurzi ukončené nějakou podmínkou. A skutečně je tomu tak, jak je vidět na příkladu <a href="recursion.xml?slajd=5">přenesení značek z pole aktuálního na pole následující</a>:
    </p>
    <examples>
        <example lang="karel">
            PŘENES
              DOKUD JE ZNAČKA
                ZVEDNI
                KROK
                POLOŽ
                VZAD
              KONEC
            KONEC
        </example>
        <example lang="karel">
            PŘENES[R]
              KDYŽ JE ZNAČKA
                ZVEDNI
                PŘENES[R]
                POLOŽ
              JINAK
                KROK
              KONEC
            KONEC
        </example>
    </examples>
    <note>
        Nerekurzivní řešení pochopitelně ještě potřebuje definici příkazu <code>VZAD</code>:
        <example lang="karel">
            VZAD
              ČELEM VZAD
              KROK
              ČELEM VZAD
            KONEC

            ČELEM VZAD
              VLEVO VBOK
              VLEVO VBOK
            KONEC
        </example>
    </note>
    <p>
        Nerekurzivní řešení se prostě ve smyčce ptá, zda na aktuální pozici je značka a pokud ano, tak ji Karel zvedne, udělá krok dopředu, položí značku a ukročí zpátky na původní pole, kde se pokusí vykonat totéž s případnou další značkou. Až tam žádná značka nebude, je cyklus opuštěn a program končí.
    </p>

</slide>
<slide title="Souvislost s rekurzí – vybrání značek">

    <p>
        Mějme za úkol vybrat z pole, na kterém Karel stojí, všechny značky. Pro srovnání <a href="recursion.xml?slajd=12">dřívější řešení rekurzí</a> a řešení pomocí cyklu s podmínkou:
    </p>
    <examples>
        <example lang="karel">
            ; A) vyber pole – s podmínkou
            VYBER
              DOKUD JE ZNAČKA
                ZVEDNI
              KONEC
            KONEC
        </example>
        <example lang="karel">
            ; B) vyber pole – rekurzí
            VYBER[R]
              KDYŽ JE ZNAČKA
                ZVEDNI
                VYBER[R]
              KONEC
            KONEC
        </example>
    </examples>
    <p>
        PS: Nejen tento příklad je pěknou ilustrací principu, že každá rekurze se dá nahradit smyčkou. (Většinou jsou ale rekurzivní algoritmy přepsané pomocí smyček výrazně nepřehlednější a neprůhlednější. I když zrovna tady to není úplně pravda :-)
    </p>

</slide>
<slide title="Souvislost s rekurzí – zaplnění značkami">

    <p>
        Nyní mějme za úkol opačný problém – zaplnit pole, na kterém Karel stojí, značkami. Pro srovnání opět <a href="recursion.xml?slajd=13">dřívější řešení rekurzí</a> a řešení pomocí cyklu s podmínkou:
    </p>
    <examples>
        <example lang="karel">
            ; A) zaplň pole – s podmínkou
            VYPLŇ
              DOKUD JE MÍSTO
                POLOŽ
              KONEC
            KONEC
        </example>
        <example lang="karel">
            ; B) zaplň pole – rekurzí
            VYPLŇ[R]
              KDYŽ JE MÍSTO
                POLOŽ
                VYPLŇ[R]
              KONEC
            KONEC
        </example>
    </examples>

</slide>
<slide title="Souvislost s rekurzí – nalezení zdi">

    <p>
        Dříve jsme již došli s Karlem rekurzivně ke zdi (dokonce <a href="recursion.xml?slajd=14">několika způsoby</a>). Nyní pro srovnání totéž ještě pomocí cyklu s podmínkou:
    </p>
    <examples>
        <example lang="karel">
            ; A) dojdi ke zdi – s podmínkou
            KE ZDI
              DOKUD NENÍ ZEĎ
                KROK
              KONEC
            KONEC
        </example>
        <example lang="karel">
            ; B) dojdi ke zdi – rekurzí
            KE ZDI
              KDYŽ NENÍ ZEĎ
                KROK
                KE ZDI
              KONEC
            KONEC
        </example>
    </examples>

</slide>
<slide title="Závěr">

    <p>
        Jelikož v Karlovi se mnoho problémů řeší elegantně rekurzí, nejsou cykly zase tak moc potřeba. Ale třebas pro zaplnění pole určeným počtem značek jsou nezastupitelné.
    </p>
    <p>
        Jiná je situace v běžných programovacích jazycích, tam cykly patří k základní „nástrojové“ výbavě. Ne že by prakticky každý problém se smyčkou nešel řešit rekurzí, ale mnoho jazyků není na rekurzi optimalizováno a kód se smyčkami je zkrátka méně náročný na hardwarové zdroje. Bohužel, často je ruku v ruce s tím také podstatně neprůhlednější.
    </p>

</slide>
<slide title="Úlohy k procvičení">

  <p>
    Většinu těchto úloh už jste řešili v <a href="overview.xml?slajd=19">první hodině Karla</a>. Nicméně spousta z nich se snadněji zapíše pomocí smyček, tak si to zkuste znovu s nimi.
  </p>
  <ol>
    <li>
      Naučte Karla příkaz na zaplnění prázdného pole, tj. položte na něj právě osm značek. Až ho bude umět, zkuste ho zavolat na poli, kde už nějaké značky jsou.
    </li>
    <li>
      Naučte Karla opačnou úlohu – vyberte z plně zaplněného pole všechny značky. A opět – až to bude umět, zkuste příkaz zavolat na poli, kde je značek méně než osm.
    </li>
    <li>
      Naučte Karla otočit se jednou kolem dokola.
    </li>
    <li>
      Za předpokladu, že Karel je před vykonáváním příkazu DOMA, dojděte s ním k protější zdi. Až to bude umět, zkuste dojít k protější zdi z jiného místa (tedy na začátku stůjte blíže ke zdi nebo naopak dále od ní).
    </li>
    <li>
      Za předpokladu, že Karel je před vykonáváním příkazu DOMA, obejděte s ním právě jednou kolem dokola celé město. (Zajistěte, aby Karlovi u zdi nepřekážely žádné překážky.)
    </li>
    <li>
      Za předpokladu, že Karel je před vykonáváním příkazu DOMA, vyplňte celou spodní řadu značkami. Přitom:
      <ul>
        <li>
          Položte na každé místo právě jednu značku.
        </li>
        <li>
          Položte na každé místo právě osm značek.
        </li>
      </ul>
    </li>
  </ol>

</slide>


</lecture>
