3. seminář - Anomymní funkce

TEACHINGZPP2

Reference na funkci

V jazyce Python je název funkce bez kulatých závorek chápán jako proměnná obsahující referenci na objekt jež danou funkci reprezentuje. Můžeme tedy udělat následující.


    # funkce
    def na_druhou(x):
      return x ** 2
      
    # volání funkce
    print(na_druhou(4)) # vypíše: 16
    
    # vytvoření nové reference na funkci na_druhou
    f = na_druhou
    
    # zavolání funkce pomocí nové reference
    print(f(4)) # vypíše: 16

Anonymní funkce

Pokud vytváříme funkce pomocí klíčového slova def, tak je funkce uložena do proměnné a to je někdy nezádoucí. Proto většina moderních jazyků umožnňuje vytvořit funkci beze jména. Takovým funkcím říkáme anonymní. Pro vytvoření anonymní funkce používáme klíčové slovo lambda:


    lambda parametr_1, parametr_2, ..., parametr_n: výraz

Kde parametr_1, parametr_2 až parametr_n jsou parametry funkce, které jsou dostupné v těle anonymní funkce tvořené jedním výrazem výraz.


    # zápis funkce pomocí def
    def f1(x, y):
      return x + y
      
    # zápis funkce pomocí lambda výrazu
    f2 = lambda x, y: x + y
    
    # obě funkce se volají stejně
    f1(4,2)
    f2(4,2)

Výchozí parametry funkce

Někdy je žádoucí, aby parametr funkce měl nějakou výchozí hodnotu.


    def f(x=1): # výchozí hodnota parametru x je 1
        return x
        
    print(f(42)) # vypíše: 42
    print(f()) # vypíše: 1 (výchozí hodnota parametru x)
    
    # Lze použít i v lambda výrazu
    f = lambda x=1: x # výchozí hodnota parametru x je 1
    
    print(f(42)) # vypíše: 42
    print(f()) # vypíše: 1 (výchozí hodnota parametru x)
    
    # parametr x nemá výchozí hodnotu, y má výchozí hodnotu 1
    def f(x, y=1):
      return x*y
      
    print(f(42, 10))
    print(f(42))
    
    def f(y=1, x):
        return x*y
   # způsobí chybu: SyntaxError: non-default argument follows default argument
   

    # vyraz seznam=[] se vyhodnotí pouze při prvním volání funkce kde neni predan argument
    def pridej_a_secti(x, seznam=[]):
      seznam.append(x)
      soucet = 0
      for i in seznam:
        soucet += i
      return soucet
      
    print(pridej_a_secti(1, [42, 43])) # vypíše: 86
    print(pridej_a_secti(42)) # vypíše: 42
    print(pridej_a_secti(43)) # vypíše: 85!

Vnořené funkce

Tělo funkce může obsahovat další funkce, které se bežně označují jako vnořené funkce (nested functions)


    # funkce
    def f1():
        # tělo funkce
        # vnořená funkce
        def f2():
            # tělo vnořené funkce
            print("Zanořená funkce")
            
        # volání vnořené funkce f2()
        f2()
    
    # volání funkce f1()
    f1()
    
    # volání funkce f2()
    f2() # způsobí chybu: NameError: name ’f2’ is not defined
    
    # Zanořovat můžeme i anonymní funkce
     def f1():
        f2 = lambda zprava: print(zprava)
        f2("Zanořená funkce")

Rozsah platnosti


    a = 42 # globální proměnná
    
    def f1():
        a = 0 # nová lokální proměnná
        print(a) # vytiskneme lokální proměnnou
        
    f1() # vypíše: 0

Funkce vyšších řá́dů̊

Jako funkce vyššího řádu se označuje funkce, která akceptuje jako parametry jiné funkce nebo vrací funkci jako návratovou hodnotu.


    # FILTROVÁNÍ
    def filtrovani(f, sekvence):
        sekvence_f = []
        for i in sekvence:
            if f(i):
                sekvence_f.append(i)
        return sekvence_f
        
    sekvence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    licha_cisla = lambda x: x % 2 != 0
    suda_cisla = lambda x: x % 2 == 0
    sekvence_licha_cisla = filtrovani(licha_cisla, sekvence)
    print(sekvence_licha_cisla) # vypíše: [1, 3, 5, 7, 9]
    sekvence_suda_cisla = filtrovani(suda_cisla, sekvence)
    print(sekvence_suda_cisla) # vypíše: [2, 4, 6, 8, 10]
    
    # Jazyk Python obsahuje funkci filter
    sekvence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    sekvence_licha_cisla = filter(lambda x : x % 2 != 0,
    sekvence)
    print(sekvence_licha_cisla) # vypíše: [1, 3, 5, 7, 9]

Funkce vracející funkci


    def vytvor_funkci_na_n(n):
        # parametr n je lokální proměnná
        def na_n(x):
            print(x ** n)
        
        # lokální funkce je vrácena jako návratová hodnota
        return na_n
        
    na_druhou = vytvor_funkci_na_n(2)
    na_druhou(4) # vypíše: 16
    na_treti = vytvor_funkci_na_n(3)
    na_treti(4) # vypíše: 64
    
    # To samé pomocí anonymní funkce
    def vytvor_funkci_na_n(n):
        return lambda x : print(x ** n)
        
    na_druhou = vytvor_funkci_na_n(2)
    na_druhou(4) # vypíše: 16

Úkoly

Napište funkci test_all(f, sekvence), která ověří, zda je předaná funkce f() pravdivá pro všechny prvky sekvence sekvence. Pokud ano, funkce test_all() vrací True, jinak False.
Napište funkci test_any(f, sekvence), která ověří́, zda je přdaná funkce f() pravdivá alespoň pro jeden prvek sekvence sekvence. Pokud ano, funkce test_any() vrací True, jinak False.
Napište funkci mapovani(f, sekvence), která na každý prvek sekvence sekvence použije funkci f() a výslednou sekvenci vrátí. Například mapovani(lambda x: x + x, [1, 2, 3, 4, 5]) vrátí seznam [2, 4, 6, 8, 10].
Napište funkci aplikuj(f, sekvence), která akceptuje operaci se dvěma operandy, jež je zadána funkcí f() a vrací výsledek postupné aplikace této operace na všechny prvky sekvencesekvence. Napríklad aplikuj(lambda x, y: x + y, [1, 2, 3, 4, 5]) sečte všechny hodnoty v [1, 2, 3, 4, 5] a vrátí výslednou hodnotu 15.