<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python</maintitle>
  <author>Jiří Znamenáček</author>
  <title>Uzávěry &amp; lambda-funkce</title>
  <date>2010-11-10</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="Uzávěry (closures) I">

  <p>
    Funkce definované uvnitř jiných funkcí mají jednu podivuhodnou vlastnost – <em>jsou-li z nadřazené funkce vráceny, nesou s sebou kontext platný v době jejich vrácení</em>. Nejlépe osvětlí příklad:
  </p>
  <example lang="python">
# toto je vnější funkce, „generující“ uzávěr
def přičti(kolik):

    # tato vnitřní funkce vidí na proměnné nadřazené funkce,
    #    tj. i na argument 'kolik'
    def vnitřní_fce(k_čemu):
        return kolik + k_čemu
    
    # vnitřní funkci vrátíme „upravenou“ o argument 'kolik'
    #    z doby jejího vrácení
    return vnitřní_fce


# „Vyrobme“ funkci, která bude ke svému argumentu přičítat 5:
>>> přičti_5 = přičti(5)
# Je to opravdu funkce, a to ta očekávaná (vnitřní):
>>> přičti_5
&lt;function vnitřní_fce at 0x00E2D108>

# Vyzkoušejme to:
>>> přičti_5(3)
8

# Na věci nic nezmění, když vyrobíme další funkci s jiným číslem:
>>> přičti_3 = přičti(3)
>>> přičti_3
&lt;function vnitřní_fce at 0x00E2D0C0>
>>> přičti_3(3)
6
>>> přičti_5(10)
15
  </example>

</slide>
<slide title="Uzávěry (closures) II">

  <p>
    Jelikož každá z vnitřních funkcí si sebou nese kontext platný v okamžiku jejich vrácení, tedy i hodnoty proměnných (a tudíž i argumentů) v nadřazené funkci v tu chvíli, odpovídají přičítací funkce z předchozího slajdu vlastně následujícím definicím:
  </p>
  <example lang="python">
def přičti_5(k_čemu):
    return 5 + k_čemu

def přičti_3(k_čemu):
    return 3 + k_čemu
  </example>
  <p>
    Takovéto funkce jsou v Python'u velmi často používány (nejkřiklavější příklad nabízí asi <a href="/materialy/python/functions/decorators.xml">dekorátory</a>) a říká se jim, případně i takovémuto chování obecně, právě <strong>uzávěry</strong> (<em>closures</em>).
  </p>

</slide>
<slide title="Poznámka">

    <p>
        Jelikož volání funkce <em>přičti()</em> vrací funkci, můžete samozřejmě napsat třeba i tohle:
    </p>
    <example lang="python">
        >>> přičti(5)(3)
        8
    </example>
    <p>
        Ne že by to tedy bylo dvakrát čitelné, zvlášť kdybyste uzávěrů za sebou zřetězili vícero…
    </p>

</slide>
<slide title="Co je to uzávěr?">

    <p>
        Ačkoli předchozí (hodně umělý) příklad snad osvětlil, jak se uzávěr chová, pro vyškolené paradigmatem OOP vymyslel před lety Steve Majewski krásný příměr:
    </p>
    <blockquote>
        Objekt je balík dat, ke kterému jsou přidané metody.<br/>
        Uzávěr je procedura, ke které jsou přidána data.
    </blockquote>
    <p>
        Jejich poměrná neznalost je asi v přímé úměře k rozšíření OOP – jejich hájemstvím jsou totiž převážně <em>funkcionální programovací jazyky</em> a těžko si představit něco víc ortogonálního k OOP než právě je.
    </p>

</slide>
<slide title="Lambda-funkce">

  <p>
    Lambda-funkce v Python'u jsou anonymní (tj. bezejmenné) funkce definované typicky v rámci jednoho řádku a skládající se pouze z jednoho výrazu, který je vyhodnocen a automaticky vrácen ve chvíli volání funkce:
  </p>
  <example lang="python">
lambda [ARGUMENT[Y]]: VÝRAZ
  </example>
  <p>
    Pythonovské <em>lambda-funkce</em> tedy nemají jméno, jejich argumenty nejsou uzavřeny v závorkách a spočítaná hodnota (či objekt) jejich těla je automaticky vrácena, aniž by bylo třeba použít příkaz <code>return</code>.
  </p>
  <note>
    Přesněji řečeno je tato definice platná pouze pro Python, protože v něm jsou lambda-funkce oproti běžně chápanému pojetí tohoto termínu velmi „osekány“. Typicky totiž těla lambda-funkcí nejsou omezena pouze na jeden jediný výraz – ve většině jazyků, které je obsahují, jsou lambda-funkce plnohodnotné funkce, které prostě jen nemají vlastní záznam v tabulce jmen programu a vyskytují se na místě, kde by jinak stál odkaz na „běžnou“ funkci.
  </note>

</slide>
<slide title="Lambda-funkce v uzávěrech">

  <p>
    Příklad z prvního slajdu o uzávěrech..
  </p>
  <example lang="python">
def přičti(kolik):
    def vnitřní_fce(k_čemu):
        return kolik + k_čemu
    return vnitřní_fce
  </example>
  <p>
    ..by šel pomocí lambda-funkce přepsat takto:
  </p>
  <example lang="python">
def přičti(kolik):
    return lambda k_čemu: kolik + k_čemu

>>> přičti_5 = přičti(5)
>>> přičti_5
&lt;function &lt;lambda> at 0x00DC9CD8>
>>> přičti_5(3)
8
  </example>
  <note>
    Asi to není na první pohled tak pěkný zápis, ale má dvě výhody:
    <ul>
      <li>
        Bez dalšího zkoumání každý vidí, že návratovým typem funkce <em>přičti()</em> je <em>funkce</em>.
      </li>
      <li>
        Do tabulky lokálních objektů funkce <em>přičti()</em> není potřeba zavádět další funkci (zde <em>vnitřní_fce()</em>), čímž se ušetří dost prostředků z „režije“ programu.
      </li>
    </ul>
  </note>

</slide>
<slide title="Použití">

    <p>
        Lambda-funkce nachází použití všude tam, kde je požadována funkce, ale přitom není potřeba kvůli tomu zavádět pojmenovanou funkci na obecnější úrovni.
    </p>
    <p>
        Vzhledem k „osekanosti“ lambda-funkcí v Python'u se s nimi tedy většinou setkáme na místech parametrů funkcí, kde je uvedené omezení na jednu řádku a jeden výraz nakonec ještě i celkem vítáno. Několik klasických případů ilustrují následující příklady.
    </p>

</slide>
<slide title="Příklady použití – řazení pomocí „sorted()“">

    <!--p>
        Typicky například jako parametry třídících funkcí:
    </p-->
    <example lang="python">
        sorted( xs, key = lambda x: x[2] )     # 

        sorted( xs, key = lambda x: len(x) )   # sorted( xs, key=len )
    </example>
    <br/>
    <note>
        Pro srovnání – první z příkladů by se pomocí pojmenované funkce zapsal takto:
        <example lang="python"> 
            def fn(x):
                return x[2]
            sorted(xs, key=fn)
        </example>
    </note>

</slide>
<slide title="Příklady použití – funkce jako parametr funkce">

    <!--p>
        Nebo jako parametry jiných funkcí očekávajících na vstupu funkci:
    </p-->
    <example lang="python">
        def mymap(xs, fn=lambda x: x):
            return [fn(x) for x in xs]

        # Výchozí chování pojmenovaného argumentu 'fn' je „identita“:
        >>> mymap( ['a', 'b', 'c', ] )
        ['a', 'b', 'c']

        # „Přepišme“ identitu vestavěnou funkcí 'ord':
        >>> mymap( ['a', 'b', 'c', ], ord )
        [97, 98, 99]

        # Podstrčme funkci nějakou naši vlastní „šílenost“:
        >>> mymap( ['a', 'b', 'c', ], lambda x: chr(ord(x)+3) )
        ['d', 'e', 'f']
    </example>
    <note>
        Funkci můžete samozřejmě volat i jako <code>mymap('abc', ord)</code> a podobně.
    </note>

</slide>
<slide title="Příklady použití – vytvořující funkce pro „defaultdict“">

    <!--p>
        Nebo jako parametr vytvořující funkce pro typ <a href="/materialy/python/types.plus/DefaultDict.xml"><em>defaultdict</em></a>:
    </p-->
    <example lang="python">
        >>> from collections import defaultdict
        >>> xs = "abrakadabra"
        >>> xd = defaultdict(
              lambda: ['samohláska', 0] if x in 'aeiouy' else ['souhláska', 0]
            )
        >>> for x in xs:
        ...   xd[x][1] += 1
        ... 
        >>> xd
        defaultdict(&lt;function &lt;lambda> at 0x7fc7473b96a8>,
          {'b': ['souhláska', 2], 
           'd': ['souhláska', 1],
           'r': ['souhláska', 2],
           'a': ['samohláska', 5],
           'k': ['souhláska', 1]}
        )
    </example>
    <note>
        (Pozalamováno pro lepší čitelnost.)
    </note>

</slide>


</lecture>
