<?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>Rekurze</title>
  <date>2011-09-22</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="Princip vykonávání příkazů I">

  <p>
    Natrefí-li Karel v těle procedury na volání neatomického příkazu (tj. čehokoliv kromě <code>KROK</code>, <code>VLEVO VBOK</code>, <code>POLOŽ</code> a <code>ZVEDNI</code>), „odskočí“ si do definice tohoto neatomického příkazu, aby se po jeho vykonání zase vrátil zpátky a pokračoval ve vykonávání příkazu původního. Příklad:
  </p>
  <example lang="karel">
VPRAVO VBOK
  ČELEM VZAD   →   ČELEM VZAD
                     VLEVO VBOK
                     VLEVO VBOK
               ←   KONEC
  VLEVO VBOK
KONEC
  </example>
  <p>
    Je zjevné, že při odskoku do procedury <code>ČELEM VZAD</code> si Karel musel zapamatovat, na jaké místo se má vrátit, aby dokázal dokončit příkaz <code>VPRAVO VBOK</code>. Přitom po návratu do procedury <code>VPRAVO VBOK</code> už zase mohl tuto informaci zapomenout.
  </p>

</slide>
<slide title="Princip vykonávání příkazů II">

    <p>
        Co kdybychom ale v těle procedury zavolali tu samou proceduru? Jako třebas:
    </p>
    <example lang="karel">
PIRUETA
  VLEVO VBOK
  PIRUETA
KONEC
    </example>
    <p>
        V tomto případě začne Karel vykonávat příkaz <code>PIRUETA</code>, který mu nejdříve předepisuje otočit se <code>VLEVO VBOK</code>. Karel se tedy poslušně otočí vlevo vbok a podívá se dál. Tam na něj ale čeká podivnost – znovu začít vykonávat příkaz <code>PIRUETA</code>? Dřív než provádění aktuálního příkazu doběhlo do konce? Nu, co se dá dělat – jak je psáno, tak je dáno. Karel tedy poslušně odskočí z prostředka právě vykonávaného příkazu a zavolá další piruetu. A v ní má předepsáno, že se má otočit doleva. Tak se tedy poslušně otočí a podívá se, co má dělat dál... Přibližně podle následujícího schématu:
    </p>
    <example lang="karel">
PIRUETA
  VLEVO VBOK
  PIRUETA      →   PIRUETA
                     VLEVO VBOK
                     PIRUETA      →   PIRUETA
                                        VLEVO VBOK
                                        PIRUETA      →   PIRUETA
                                                           ...
    </example>
    <p>
        Na první pohled to možná vypadá jako nesmysl, ale uvedený program skutečně funguje – Karel se točí pořád doleva a nikdy sám od sebe nepřestane<sup>*</sup>, musíme ho zastavit ručně klikem na červený křížek v knihovně známých příkazů.
    </p>
    <note>
        <sup>*</sup> Samozřejmě pouze teoreticky – viz <a href="?slajd=4">rozbor dále</a>.
    </note>
    <p>
        PS: Někdy se toto chování může hodit, ale většinou spíše ne. Zjevně by se nám tedy v obecném případě hodila nějaká podmínka, která zajistí, že řetězec nekonečných volání se někdy ukončí.
    </p>

</slide>
<slide title="Rekurze">

    <p>
        Uvedené technice, kdy příkaz volá sám sebe – tedy je v podstatě nadefinován sám sebou – se odborně říká <strong>rekurze</strong>. (Stejně jako v matematice, kde je rekurzivní zadávání nejrůznějších posloupností zcela běžné.) Přitom:
    </p>
    <ul>
        <li>
            je-li příkaz definován sám sebou, mluvíme o tzv. <em>přímé rekurzi</em>;
        </li>
        <li>
            pokud je volání příkazu schováno v posloupnosti volání dalších příkazů, nazývá se taková <em>rekurze nepřímou</em>.
        </li>
    </ul>
    <p>
        Na první pohled to trochu vypadá jako nějaká děsivá a zakázaná definice kruhem a vlastně skoro i je, ale funguje to a spousta problémů se pomocí rekurze popisuje velmi snadno. Navíc v případě našeho Karla, který neobsahuje žádné tzv. <em>proměnné</em>, nám rekurze umožňuje tento „nedostatek“ obejít a dokonce Karla jistým způsobem naučit počítat, jak si v dalším ukážeme.
    </p>

</slide>
<slide title="Paměťová náročnost">

  <p>
    Když Karel z vnitřku procedury volá proceduru jinou, musí si zapamatovat příslušné návratové informace. Je zjevné, že v případě rekurze <a href="?slajd=2">z předchozího slajdu</a> se paměť bude (může) plnit nikdy neodstraněnými údaji:
  </p>
  <example lang="karel">
PIRUETA   ; 1. volání
  VLEVO VBOK
  PIRUETA      →   PIRUETA   ; 2. volání
                     VLEVO VBOK
                     PIRUETA      →   PIRUETA   ; 3. volání
                                        VLEVO VBOK
                                        PIRUETA      →   PIRUETA   ; 4. volání
                                                           ...
  </example>
  <note>
    Pro přehlednost jsem pomocí komentářů doplnil schéma z předchozí stránky o pořadová čísla jednotlivých volání procedur <code>PIRUETA</code>.
  </note>
  <p>
    Zjevně při každém rekurzivním volání si Karel musí zapamatovat návratové informace do volání předchozího – z druhé pirety se musí umět vrátit do první, ze třetí do druhé a tak dál. A jelikož definice procedury <code>PIRUETA</code> neobsahuje žádnou ukončovací podmínku, bude si muset těchto informací pamatovat čím dál tím více... Nebo ne?
  </p>

</slide>
<slide title="Paměťová náročnost – koncová rekurze">

  <p>
    Obecně některé programovací jazyky dokáží vyhodnotit takto specifická rekurzivní volání jako nenávratová a žádné návratové informace ani nebudou zaznamenávat, protože přijdou na to, že je to zbytečné. Rozhodně na to však nesázejte ^_^
  </p>
  <note>
    Konkrétně jde o problém tzv. <em>koncové rekurze</em> (<em>tail recursion</em>). Některé jazyky (mnoho funkcionálních to má přímo v definici jazyka) dokážou rozpoznat, že rekurzivní volání je posledním příkazem v těle aktuální procedury a že se do ní tudíž nebude třeba nikdy vracet. V takovémto případě nahradí rekurzivní volání prostě přímým voláním dalšího (stejného) příkazu a žádné návratové informace ukládat nebudou. (PS: Python zrovna tohle nedělá a z praktických důvodů je na počet rekurzivních kroků nastaven systémový limit.)
  </note>
  <p>
    Každopádně nenávratová rekurze v Karlovi zas tolik použití stejně nemá, daleko častěji se z rekurze po splnění nějaké podmínky budeme chtít začít vracet po jednotlivých voláních zpátky. Každý návrat do předchozího volání umaže z tabulky návratových hodnot další informaci (podobně jako když jsme se po vykonání příkazu <code>ČELEM VZAD</code> vrátili zpátky do procedury <code>VPRAVO VBOK</code>) a počet zaznamenaných návratových dat se tak bude postupně snižovat, až se po návratu na počátek rekurzivního volání (samozřejmě neopustíme-li ho z nějakého jiného důvodu dříve) sníží na nulu.
  </p>

</slide>
<slide title="Obecná rekurze">

  <p>
      Dostaňme s Karlem za úkol přenosit značky z aktuálního pole na pole následující. S rekurzí vypadá řešení na první pohled možná až magicky, ale už na druhý je vlastně celkem přirozené:
  </p>
  <example lang="karel">
PŘENES[R]
  KDYŽ JE ZNAČKA
    ZVEDNI
    PŘENES[R]
    POLOŽ
  JINAK
    KROK
  KONEC
KONEC
  </example>
  <note>
    Zde jsem sugestivně označil rekurzivní proceduru pomocí <code>[R]</code>, aby se nám nepletla s <a href="?slajd=8">nerekurzivním řešením</a>. Ale je to prostě jen pojmenování. Stejně tak dobře se mohla jmenovat třeba <code>PŘENES S REKURZÍ</code> nebo jakkoliv jinak.
  </note>

</slide>
<slide title="Obecná rekurze – vysvětlení">

  <p>
    Co se v tomto případě děje? Zkusme si to pro případ dvou značek na výchozím poli (abychom se neukreslili) rozepsat do nám již známého schématu:
  </p>
  <example lang="karel">
PŘENES[R]   ; 1. volání
  KDYŽ JE ZNAČKA
    ZVEDNI
    PŘENES[R]      →   PŘENES[R]   ; 2. volání
                         KDYŽ JE ZNAČKA
                           ZVEDNI
                           PŘENES[R]      →   PŘENES[R]   ; 3. volání
                                                ...
                                                JINAK
                                                  KROK
                                                KONEC
                                          ←   KONEC
                           POLOŽ
                         JINAK
                         ...
                   ←   KONEC
    POLOŽ
  JINAK
  ...
KONEC
  </example>
  <p>
    Tohle už je složitější, tak si to probereme postupně:
  </p>
  <ol>
    <li>
      Při prvním zavolání příkazu/procedury <code>PŘENES[R]</code> Karel zjistil, že na políčku je nějaká značka (zatím jsou tam dvě), proto se aktivovala část rozhodovacího příkazu <code>KDYŽ JE ZNAČKA</code>. V ní Karel poslušně jednu značku zvedl (<code>ZVEDNI</code>) a vzápětí narazil na rekurzivní volání procedury <code>PŘENES[R]</code>. Proto ji zavolal (viz druhý sloupec <code>PŘENES[R](2)</code>).
    </li>
    <li>
      Nyní je Karel opět na začátku procedury, tedy u podmínky <code>KDYŽ JE ZNAČKA</code>. Jelikož je na políčku ještě pořád značka (tentokrát už poslední), opět se aktivuje stejná část rozhodovacího příkazu a Karel poslušně jednu značku zvedne (<code>ZVEDNI</code>) a stejně jako předtím narazí na další rekurzivní volání procedury <code>PŘENES[R]</code>. Proto ji zavolá (viz třetí sloupec <code>PŘENES[R](3)</code>).
    </li>
    <li>
      Ve třetím rekurzivním volání již podmínka <code>JE ZNAČKA</code> splněna není – Karel obě značky již zvedl v předchozích dvou voláních procedury <code>PŘENES[R]</code>. Karel proto zaběhne do druhé větve rozhodovacího příkazu, tj. <code>JINAK</code>. Zde má předepsaný <code>KROK</code>, proto ho poslušně udělá (čímž se posune o jedno pole před sebe).
    </li>
    <li>
      Nyní se začne uplatňovat návrat z rekurzivních volání. Karel dokončí aktuální příkaz <code>PŘENES[R](3)</code> a vrátí se do předchozího rekurzivního kroku <code>PŘENES[R](2)</code> na místo, ve kterém ho opustil, tzn. za příslušné volání <code>PŘENES[R]</code>.
    </li>
    <li>
      Zde má předepsáno položit značku (<code>POLOŽ</code>), proto ji položí. Tím ovšem končí zpracování větve <code>KDYŽ JE ZNAČKA</code>, Karel proto ukončí i tento příkaz <code>PŘENES[R](2)</code> (větev <code>JINAK</code> se ve druhém rekurzivním volání nemůže uplatnit, protože podmínka nebyla splněna) a vrátí se do předchozího rekurzivního kroku <code>PŘENES[R](1)</code> na místo, ve kterém ho opustil, tzn. za příslušné volání <code>PŘENES[R]</code>.
    </li>
    <li>
      Zde ho čeká opět příkaz k položení značky <code>POLOŽ</code>, což udělá, a následně již pouze úplný konec vykonávání příkazu <code>PŘENES[R]</code>, protože ze stejných důvodů jako v předchozím kroku se větev <code>JINAK</code> nezavolá.
    </li>
  </ol>
  <p>
    V důsledku se část příkazů před rekurzivním voláním (zde <code>ZVEDNI</code>) provede stejným počtem krát, zde tedy dvakrát, jako část příkazů za rekurzivním voláním (zde <code>POLOŽ</code>) a Karel tedy nejdříve značky vysbírá, aby jich poté o políčko dál (aktivace větve <code>JINAK–KROK–KONEC</code>) zase stejný počet položil.
  </p>

</slide>
<slide title="Obecná rekurze – poučení (důležitá!)">

  <p>
    Poučení z této lekce o rekurzi jsou dvě a obě jsou velmi důležitá, takže si je zapište za uši:
  </p>
  <ul>
    <li>
      <strong>Část za rekurzivním voláním se provádí stejným počtem krát, jako část před ním.</strong>
      <note>Pokud se do toho nenabouráte nějakou dodatečnou podmínkou.</note>
    </li>
    <li>
      <strong>V některém z rekurzivních kroků musí podmínka zajistit, že k dalšímu rekurzivnímu volání již nedojde.</strong>
      <note>V našem příkladu došly na prvním políčku značky, což testovala podmínka <code>KDYŽ JE ZNAČKA</code>.</note>
    </li>
  </ul>

</slide>
<slide title="Kotva rekurze">

    <p>
        Podmínka, která rozhoduje o tom, zda v dalším kroku opět zavoláme rekurzi, nebo se z ní začneme vracet zpět k prvnímu volání, se odborně nazývá <strong>kotva rekurze</strong>. Je vlastně tím, co mění nekonečnou rekurzi na konečnou (tedy „ukotvenou“ :-).
    </p>
    <p>
        V místě, kde se <em>kotva rekurze</em> uplatní, tedy v druhé větvi podmínky, může být libovolný kód. (V případě potřeby dokonce i žádný!) Což má samozřejmě nejrůznější použití. Kupříkladu Karel v <a href="?slajd=6">předchozím příkladu</a> nemusí přenosit značky jen o políčko dál, ale třebas o dvě nebo tři:
    </p>
    <example lang="karel">
        PŘENES[R]
          KDYŽ JE ZNAČKA
            ZVEDNI
            PŘENES[R]
            POLOŽ
          JINAK
            KROK
            KROK   ; přidali jsme jeden krok navíc
          KONEC
        KONEC
    </example>

</slide>
<slide title="Vliv změny v okolí rekurze">

    <p>
        Je důležité si uvědomit, že blok s podmínkou (ze <a href="?slajd=6">stejného příkladu</a>)..
    </p>
    <example lang="karel">
        KDYŽ JE ZNAČKA
          ZVEDNI
          PŘENES[R]
          POLOŽ
        KONEC, JINAK
    </example>
    <p>
        ..se totiž <strong>vždy provede celý, ale každá z jeho dvou částí v jinou dobu</strong>! Pokud je podmínka postupně splněna třebas <em>n</em>-krát, tak nejdříve se <em>n</em>-krát provede část kódu před rekurzivním voláním (zde <code>ZVEDNI</code>) a teprve potom se také <em>n</em>-krát provede část kódu po něm (zde <code>POLOŽ</code>).
    </p>
    <note>
        V ukázkovém kódu nejdříve Karel sesbíral z políčka, na němž stál, všechny značky (a nemusel vědět, kolik jich tam je), následně udělal krok dopředu a na dalším políčku stejný počet značek prozměnu položil.
    </note>
    <p>
        Ve výsledku sice Karel neumí počítat s čísly, ale zato ví, že <strong>celý kód v bloku před rekurzivním voláním se provede stejněkrát jako celý kód v bloku za rekurzivním voláním a že tam dokonce některá z těchto částí ani nemusí být</strong> (<em>n</em>-krát se pak v podstatě provádí nic). A to je vlastnost doslova k nezaplacení, jak si ukážeme v dalších příkladech.
    </p>
    <note>
        PS: Obecně by samozřejmě v kotvě rekurze, resp. někde v návratovém řetězci, mohla být další podmínka, která by rekurzi předčasně ukončila, takže žádný z bloků, resp. část z nich, by se neprovedl celý (část za rekurzivním voláním by byla vynechána). Ale smysl je myslím jasný.
    </note>

</slide>
<slide title="Příklad – jak spočítat polovinu I">

  <p>
    Následující použití rekurze pro naučení Karla jak dojít do poloviny vzdálenosti mezi ním a stěnou, ke které je natočen, vypadá na první pohled doslova magicky. Ale po vysvětlení z předchozího slajdu je vlastně zcela přirozené. Zkusme k němu dojít po jednotlivých krocích – co musí Karel udělat, aby došel do poloviny vzdálenosti?
  </p>
  <p class="enumerate">
    Jako programátor můžeme podvádět a podívat se, kolik políček Karlovi ke stěně zbývá, a pak jednoduše napsat cyklus s příslušným počtem opakování kroků. To je ovšem těžce neuniverzální řešení, protože stačí Karla postavit do jiné vzdálenosti a náš algoritmus beznadějně selže. Musíme na to tedy jinak.
  </p>
  <p class="enumerate">
    Kdyby Karel uměl počítat, nechali bychom ho dělat kroky směrem ke stěně a počítali bychom, kolik jich udělal. Potom už bychom se jednoduše o polovinu z nich vrátili zpátky. Tohle by pravděpodobně bylo řešení, které byste použili pro podobný problém v mnoha jiných jazycích. Náš Karel však počítat neumí (neobsahuje žádné <em>proměnné</em>, jejichž hodnotu bychom mohli postupně upravovat), takže na to musíme jinak.
  </p>
  <p class="enumerate">
    Ale pozor, <a href="?slajd=10">právě jsem si přece ukázali</a>, že jistým způsobem Karel počítat přeci jen nějak umí – část příkazů (ze stejného bloku) před rekurzivním voláním i po  něm se provede stejný počet krát! Pravda, nevíme <em>kolikrát</em> se tyto části provedou, jen víme, že oba počty budou stejné... Ale – nestačí nám to už?
  </p>
  <p>
    Vzpomeňmě si na předchozí návrh řešení – dojít ke stěně a pak se vrátit o polovinu počtu kroků zpátky. Nešlo by tohle nějak zařídit rekurzí? Chceme vlastně po Karlovi, aby po rekurzivním volání udělal dvakrát méně kroků než před ním... Jelikož půlkrok udělat neumíme (Karel se dokáže vždy posunout pouze o celé políčko) a musíme se tedy vracet po jednom celém kroku, jak dosáhneme požadovaného efektu? Ano, správně – dopředu (směrem ke stěně) půjdeme vždy o kroky dva! Tím dosáhneme přesně žádaného – <strong>Karel při cestě zpátky udělá vždy jeden krok na každé dva kroky při cestě tam</strong>.
  </p>

</slide>
<slide title="Příklad – jak spočítat polovinu II">

  <p>
    Rekurze však musí někdy skončit a začít se vracet na začátek. V tomto případě je to jednoduché – Karel se zarazí a začne vracet ve chvíli, kdy dojde ke zdi. Použijeme tedy podmínku <code>ZEĎ</code>. Navíc musíme ovšem ještě počítat s tím, že Karel by mohl nepozorným krokem nabourat do zdi, celé řešení bude proto vypadat následovně:
  </p>
  <example lang="karel">
; pomocné procedury

KROK?   ; bezpečný krok vpřed
  KDYŽ NENÍ ZEĎ
    KROK
  KONEC
KONEC

ČELEM VZAD   ; otočení čelem vzad
  RYCHLE
    VLEVO VBOK
    VLEVO VBOK
  KONEC
KONEC

ÚKROK VZAD   ; úkrok zpátky
  RYCHLE
    ČELEM VZAD
    KROK
    ČELEM VZAD
  KONEC
KONEC


; dovedení Karla do poloviny vzdálenosti mezi ním a zdí
PŮLKA
  KDYŽ NENÍ ZEĎ
    KROK
    KROK?
    PŮLKA
    ÚKROK VZAD
  KONEC
KONEC
  </example>

</slide>
<slide title="Příklad – jak spočítat polovinu III">

  <p>
    Rozkresleme si opět, jak bude vypada průběh programu v případě, že Karel stál na začátku na pátém políčku od zdi:
  </p>
  <example lang="karel">
PŮLKA   ; 1. volání
  KDYŽ NENÍ ZEĎ
    KROK
    KROK?
    PŮLKA         →   PŮLKA   ; 2. volání
                        KDYŽ NENÍ ZEĎ
                          KROK
                          KROK?
                          PŮLKA         →   PŮLKA   ; 3. volání
                                        ←   KONEC
                          ÚKROK VZAD
                        KONEC
                  ←   KONEC
    ÚKROK VZAD
  KONEC
KONEC
  </example>
  <p>
    Průběh výpočtu (provádění programu) je tedy následující:
  </p>
  <ol>
    <li>
      Na začátku stojí Karel na <em>pátém</em> políčku od zdi. Podmínka <code>KDYŽ NENÍ ZEĎ</code> je tedy splněna a Karel udělá poslušně dva kroky dopředu (první <code>KROK</code> „natvrdo“, protože víme, že nestojí před zdí, druhý <code>KROK?</code> bezpečně, protože se předchozím krokem ke zdi dostat mohl). Následně rekurzivně zavolá sám sebe (<code>PŮLKA(2)</code>).
    </li>
    <li>
      Druhé volání procedury <code>PŮLKA(2)</code> začíná opět podmínkou <code>KDYŽ NENÍ ZEĎ</code>. Jelikož Karel teď stojí na <em>třetím</em> políčku od zdi, udělá opět dva kroky (<code>KROK</code> a <code>KROK?</code>), čímž se dostane na <em>první</em> políčko před zdí a zavolá znovu sám sebe (<code>PŮLKA(3)</code>).
    </li>
    <li>
      Při třetím rekurzivním volání procedury <code>PŮLKA(3)</code> už podmínka <code>KDYŽ NENÍ ZEĎ</code> splněna nebude – Karel totiž už stojí přímo před zdí. Procedura se tudíž rovnou ukončí (není zde žádná větev <code>JINAK</code>, která by se mohla aplikovat) a rekurze se začne vracet na začátek.
    </li>
    <li>
      Při prvním návratu do procedury <code>PŮLKA(2)</code> na Karla čeká vykonání jednoho příkazu <code>ÚKROK VZAD</code>, čímž se posune o jedno políčko zpátky na <em>druhé</em> pole od zdi. Následně procedura <code>PŮLKA(2)</code> končí a po rekurzi se vracíme do předchozího kroku.
    </li>
    <li>
      Nyní už jsme v prvním volání <code>PŮLKA(1)</code>, kde na Karla čeká další příkaz <code>ÚKROK VZAD</code>, kterým se dostane na <em>třetí</em> políčko od zdi, a po něm už úplný konec vykonávání příkazu <code>PŮLKA</code>.
    </li>
  </ol>
  <p>
    PS: Je jasné, že Karel provede celý počet <em>dvoukroků</em> dopředu pouze tehdy, bude-li mezi ním a zdí právě sudý počet políček. Jinak udělá dopředu právě o jeden krok méně a dozadu se tudíž vrátí jaksi o jedno pole dál, než by to na první pohled mělo být. Lichý počet polí prostě na celé poloviny nerozložíme, takže to tak dopadnout musí.
  </p>

</slide>
<slide title="Poznámka k nekonečné rekurzi">

  <p>
    Pokud rekurze neobsahuje žádnou podmínku, na kterou by skončila, stává se nekonečnou:
  </p>
  <examples>
    <example lang="karel">
      PIRUETA
        VLEVO VBOK
        PIRUETA
      KONEC
    </example>
    <example lang="karel">
      VRŤ SE
        VLEVO VBOK
        VPRAVO VBOK
        VPRAVO VBOK
        VLEVO VBOK
        VRŤ SE
      KONEC
    </example>
  </examples>
  <p>
    Všimněte si především, že v obou příkladech je rekurzivní volání posledním, které se z každé iterace programu vykoná, a Karel se pak na toto místo již nikdy nevrátí! <strong>Pokud by za ním byl nějaký kód, nebude se nikdy provádět.</strong> Není vůbec problém něco takového přehlédnout a pak se divit, proč se náš program chová úplně jinak, než jsme si vymysleli.
  </p>
  <!--note>
    A opět existují programovací jazyky, které si dokáží poradit s tímto případem – v podstatě 
  </note-->

</slide>
<slide title="Ukázka – vybrání značek">

  <p>
    Mějme za úkol vybrat z pole, na kterém Karel stojí, všechny značky:
  </p>
  <example lang="karel">
    VYBER[R]
      KDYŽ JE ZNAČKA
        ZVEDNI
        VYBER[R]
      KONEC
    KONEC
  </example>
  <p>
    Karel jednoduše zvedne značku, pokud na políčku nějaká je, a rekurzí to bude dělat tak dlouho, dokud je všechny nevysbírá. Tím se rekurze ukončí (podmínka <code>JE ZNAČKA</code> již nebude nadále splněna) a vrátí se postupně do všech předchozích volání. A jelikož po rekurizvním volání již žádný další kód nenásleduje, program spořádaně skončí.
  </p>

</slide>
<slide title="Ukázka – zaplnění značkami">

  <p>
    Úplně stejně se dá vyřešit opačný problém – pole značkami prozměnu zaplnit:
  </p>
  <example lang="karel">
    VYPLŇ[R]
      KDYŽ JE MÍSTO
        POLOŽ
        VYPLŇ[R]
      KONEC
    KONEC
  </example>
  <p>
    Karel prostě rekurzivně stále pokládá značky, dokud se na políčko ještě nějaké vejdou. Jakmile je pole plně obsazeno (tedy podmínka <code>JE MÍSTO</code> již nadále není pravdivá), vrátí se rekurzivní volání postupně zpět a program se ukončí.
  </p>
  <note>
    PS: Tohle v originální <a href="https://karel.oldium.net" class="external">webové verzi Karla</a> nefunguje – postrádá podmínku <code>MÍSTO</code>.
  </note>

</slide>
<slide title="Ukázka – nalezení zdi">

  <p>
    Nyní zkusme pomocí rekurze dojít s Karlem ke zdi. A rovnou vícero variantami:
  </p>
  <example lang="karel">
    ; standardní řešení
    KE ZDI
      KDYŽ NENÍ ZEĎ
        KROK
        KE ZDI
      KONEC
    KONEC

    ; standardní řešení s uvedením nepovinné větve podmínky
    KE ZDI
      KDYŽ NENÍ ZEĎ
        KROK
        KE ZDI
      JINAK
      KONEC
    KONEC

    ; otočení podmínky
    KE ZDI
      KDYŽ JE ZEĎ
      JINAK
        KROK
        KE ZDI
      KONEC
    KONEC
  </example>
  <p>
    První varianta je asi nejpřímější a zcela v duchu předchozích příkladů. Nicméně především ta třetí, s obrácenou podmínkou, stojí za pozornost – dosti často se totiž stává, že problém, který je s nějakou podmínkou těžko řešitelný, se může ukázat mnohem snadnější s podmínkou opačnou (pokud je možno řešení příslušným způsobem obrátit). Zkuste si to zapamatovat, třeba se vám to někdy bude hodit.
  </p>

</slide>
<slide title="Úlohy k procvičení">

  <p>
    S rekurzí můžeme řešit novou velkou celou třídu problémů (v podstatě by se dalo říct, že Karel bez rekurze by ani nebyl Karel, protože by spoustu věcí nedokázal). Podobně jako dřív si také vyřešíme některé starší úlohy jiným způsobem, pomocí rekurze.
  </p>
  
  <p class="enumerate">
    Pořádná rekurzivní zadání:
  </p>
  <ol>
    <li>
      Postavte Karla do dvojnásobné vzdálenosti od zdi. (Přímá aplikace příkladu ze slajdů, jen opačným směrem ^_~)
    </li>
    <li>
      Ještě jednou na podobné brdo – posuňte Karla do třetinové vzdálenosti k nejbližší zdi. (Třetina blíže ke zdi.)
    </li>
    <li>
      Když už teď umíte rekurzi, vyražte s Karlem směrem, kterým je otočený, a zastavte se na nejbližší značce, aniž byste při tom nabourali do zdi.
    </li>
    <li>
      Zopakujte si předchozí úlohu, ale zkuste s Karlem projít celou řádku či sloupec. (Tedy nenatrefí-li Karel na značku směrem <em>tam</em>, u zdi se otočí a zkusí ji najít ještě směrem <em>zpět</em>. To pro případ, že byla na začátku za jeho zády.)
    </li>
    <li>
      Udělejte s Karlem tolik kroků, na kolika značkách stojí. Značky při tom zrušte.
    </li>
    <li>
      Dojděte s Karlem ke zdi a na poslední pole postavte tolik cihel (značek :-), kolik jste ušli kroků.
    </li>
    <li>
      Dojděte s Karlem ke zdi a zpět na výchozí pole.
    </li>
    <li>
      Dojděte s Karlem do středu (prázdného) města.
    </li>
    <li>
      Zinvertujte počet značek na aktuálním poli. (Tedy byly-li na políčku značky tři, po vykonání příkazu jich tam bude pět a naopak.)
    </li>
    <li>
      Přesuňte Karla tak, aby se prohodily vzdálenosti ke zdi před ním a za ním. (Tedy je-li na začátku před Karlem políček šest a za ním tři, po provedení příkazu jich bude šest za ním a před ním tři.)
    </li>
    <li>
      Postavte na pole před Karlem tolik značek, kolik jich je na výchozím poli. Přitom:
      <ul>
        <li>
          Značky na výchozím poli zrušte.
        </li>
        <li>
          Značky na výchozím poli zachovejte.
        </li>
      </ul>
    </li>
    <li>
      Prohoďte počty značek na dvou sousedních polích. (Za sousední pokládejte aktuální a to před ním, ať si vystačíme pouze s krokem dopředu a dozadu.)
    </li>
    <li>
      Nechť je na výchozím poli několik značek. Postavte s Karlem na každé další pole o jednu značku méně (aneb postavte schody).
    </li>
    <li>
      Dojděte s Karlem ke zdi, položte u ní minu (tj. značku ^_^) a vraťte se zpátky na startovní pole. Přitom:
      <ul>
        <li>
          Neznačkujte cestu.
        </li>
        <li>
          Na každé pole cestou položte jednu značku.
        </li>
      </ul>
    </li>
    <li>
      Dojděte s Karlem na první pole, kde je víc než jedna značka. (Asi budete potřebovat i příkaz <code>STOP</code>.)
    </li>
  </ol>
  
  <p class="enumerate">
    Variace na cykly a podmínky:
  </p>
  <ol>
    <li>
      Obcházejte s Karlem donekonečna po obvodu města.
    </li>
    <li>
      Projděte s Karlem celé město z DOMOVA řádku po řádce.
    </li>
    <li>
      Postavte šachovnici. (Žádná značka představuje bílé pole, jedna značka černé. Město je na začátku prázdné.)
    </li>
    <li>
      Zinvertujte šachovnici z předchozího kroku. (Tedy prohoďte černá a bílá pole.)
    </li>
    <li>
      Nechť je na začátku Karel na libovolném políčku v prázdném městě natočený libovolným směrem. Nechť je domov umístěn na libovolném políčku ve městě. Dojděte s Karlem do domu.
    </li>
  </ol>

</slide>


</lecture>
