title end end

5. Opakované výpočty

  1. Funkce print
  2. Příkaz return
  3. Smyčka for..
  4. Podmínka while
  5. Podmínky if.. elif.. else
    1. Podmínka if ..
    2. Podmínka if .. else
    3. Podmínka if .. elif .. else
    4. Procedura 'match' a 'case'
    5. Vnořené podmínky
    6. Příkaz break
    7. Příkaz continue
  6. Funkce len
  7. Funkce range
  8. Funkce enumerate
  9. Funkce filter, map a zip
  10. Funkce reduce
  11. Newtonova metoda
  12. Cvičení

5.1 Funkce print()

Funkce print() se všemi svými parametry má tento tvar:

print(*objekty, sep="", end="\n", file=sys.stdout, flush=False)

Kdo si přečetl odstavec 4.2.3 Parametry a argumenty *args, ví že parametr *objekty je sběrný enticový parametr a všechny ostatní parametry lze ignorovat, neboť to jsou parametry s počáteční hodnotou, neboli párové parametry.
Takže se nám povinný tvar volání funkce smrskne na:

print(*objekty)
přičemž i tento parametr lze vynechat a v tom případě nám funkce vytiskne prázdný řádek.
Doporučovanou variantou funkce print() je příkaz:
>>> print("\n")                  # vytiskne 1 prázdný řádek        
Případně
>>> print(3*"\n")                # vytiskne 3 prázdné řádky

Funkce print vyvolá vyhodnocení jednotlivých argumentů v závorkách:

>>> print("Je mi", 12 + 9, "let.")   # (string, výraz, string)
Je mi 21 let.                        # fce print() odebrala  apostrofy 

Použitelnou alternativou k funkci print je funkce sys.stdout.write():

51_stdout.py
import sys
s = ['Python', 'je', 'bezvadný']
for e in s:                # tisk na témže řádku
    sys.stdout.write('\n')     
for e in s:                # každý element na jiném řádku
    sys.stdout.write(e + '\n')
>>> %Run 51_stdout.py
 Python je bezvadný 
Python
je
bezvadný

Změnou argumentu end="\n" na end=" " lze zajistit pokračování tisku na témže řádku, přičemž rozestupem uvozovek lze ovlivnit rozestup mezi jednotlivými hodnotami výstupu.
Změnou argumentu sep="" lze přikázat typ oddělovače, případně vzdálenost mezi prvky entice - například sep=' ~ ':

>>> print('a', 'b', 'c', 'd')
a b c d
>>> print('a', 'b', 'c', 'd', sep=' ~ ', end=' !!')
a ~ b ~ c ~ d !!       

Vhodné je také vědět, že příkaz k tisku funkce print() vrací None:

>>> print(print("Nazdar"))
Nazdar
None     # Po výstupu "Nazdar" přišel na řadu příkaz print()

5.2 Příkaz return

Ve funkci s větvenými podmínkami s výhodou použijeme příkaz return:

def filtr(x):                       
    if x <= 0:                            
        return "Přirozené číslo, prosím"  
    return x                              

Protože jsou tyto příkazy v alternativních podmínkách, provedou se pouze jednou - jakmile se jeden z příkazů return provede, funkce končí bez provádění následných příkazů.

Kód, který je zapsán za příkazem return i kterékoliv další místo, kam se tok programu nedostane, se nazývá mrtvý kód.

Je dobré aby každá možná cesta funkcí narazila na příkaz return. V následující verzi absolute_value toto zajištěno není:

def absolute_value(x):
    if x < 0:
        return -x
    elif x > 0:
        return x

Tato verze není zcela správná, protože bude-li x rovno nule, žádná z podmínek není pravdivá a funkce končí, aniž by narazila na příkaz return; výslednou hodnotou je speciální hodnota zvaná None typu NoneType:

>>> print(absolute_value(0))
None

Příkaz return se použije pouze uvnitř funkce, nikoliv mimo funkci a nikoliv uvnitř definice třídy.

Příkaz 'print' versus 'return'

Příkaz print má formát funkce a slouží k zobrazení zadaného obsahu uvnitř funkce i mimo funkci.

>>> print("Nazdárek Kašpárek")     # použití mimo funkci
>>> def podíl(x, y):               # použití uvnitř funkce
        print(x / y)
>>> podíl(8, 3)                    # invokace funkce
2.6666666666666665

Příkaz return ukončí provádění funkce a vrátí vyhodnocený výraz (expression), je-li k dispozici. Není-li co vracet, vrací se implicitní hodnota "None".
Tato hodnota se však v shellu Pythonu či aplikace Thonny nezobrazuje. Chceme-li hodnotu None v shellu přesto zobrazit, použijeme příkaz print():

>>> def add_one(x):              # funkce bez výstupu
    result = x + 1

>>> add_one(8)		         # výstup "None" se nekoná 

>>> print(add_one(8))				 
 None                            # zde ano 

5.3 Smyčka for

Smyčka for .. in si pro zadaný kontejner (sekvenci či kolekci) vytvoří vlastní iterátor, s nímž provádí iteraci po prvcích objektu:

for i in container: do something	
st = {3.6, "cat", True}             # kolekce typu set
for i >in st:
    print(i, end=" ")
>>> True 3.6 cat 

Na výstupu idiomu for vidíme, že pořadí elementů se změnilo, protože kolekce set pořadí změnit může, stejně jako kolekce frozenset.

Iterace slovníku (dict) vrací pouze klíče slovníku:

sl = {1: "uno", 2: "duo", 3: "tre"}  # kolekce typu dict

for i in sl:
    print(i, end=" ")
>>> 1 2 3 

Případně:

sl = {1: "uno", 2: "duo", 3: "tre"}     # kolekce typu dict

for i,j in sl.items():   # dict.items() je vestavěná methoda
    print(i,j, end=" ")
>>> 1 uno 2 duo 3 tre  

O tom, že naše kontejnery jsou vybaveny metodou __iter__ a že to tedy jsou iterábly, se přesvědčíme funkcí

dir(zkoumaný_objekt)

jejímž výstupem je seznam metod a funkcí zadaného objektu, v němž metodu __iter__ nalezneme.

Stejně jako smyčka for.. si vlastní iterátor vytvářejí funkce open(), min(), max(), map(), filter(), zip(), enumerate() a reversed().

Funkce open(...) s vlastním iterátorem:

>>> f = open("F:/Howtopy/files/week.txt")  # iterátor pro week.txt 

>>> next(f)                         # první řádek souboru
'pondělí\n'
>>> next(f)                         # další řádek souboru
'úterý\n'                                 
>>> for i in f: print(i, end=" ")   # zbývající řádky souboru
 středa
 čtvrtek
 ...
>>> f.close()                       # zavře soubor - viz kap.7.3

Následující příklad ukazuje použití zřetězení (concatenation) a smyčku for pro tvorbu abecedních řad. Například, v knize Roberta McCloskeyho: Uvolněte cestu pro káčata se kachňata jmenují: Jack, Kack, Lack, Mack, Nack, Ouack, Pack a Quack. Tato smyčka vydá jejich jména v pořadí:

prefixes = "JKLMNOPQ"
suffix = "ack"

for letter in prefixes:
    print(letter + suffix, end=" ")

Výstup programu je tento:

>>> Jack  Kack  Lack  Mack  Nack  Oack  Pack  Qack

V pořádku to úplně není, protože Ouack a Quack nejsou zapsáni správně. Tuto chybu napravíme v rámci cvičení 6.13.1.


5.4 Podmínka  while

Opakovaný výpočet pro jednotlivé elementy iteráblu lze s výhodou provést pomocí smyčky while:

Použití podmínky while si ukážeme na funkci countdown:

def countdown(n):      # parametr n jako počáteční stav počítadla
    while n > 0:                    # omezující podmínka
        print(n, end=" ")
        n = n-1                     # změna stavu počítadla

Příkaz while se dá číst téměř jako prostý text: Pokud bude n > 0, vytiskni hodnotu n a potom ji zmenši o 1. Když se dostaneš k hodnotě n = 0, hodnocení podmínky končí.

Formálněji popíšeme tok výpočtu s příkazem while takto:

  1. Vyhodnoť podmínku while s výstupem False nebo True.
  2. Je-li hodnocení False, vystup ze smyčky while a pokračuj dalším příkazem (pokud existuje).
  3. Je-li hodnocení True, proveď všechny příkazy v těle podmínky a vrať se ke kroku 1.

Tělo podmínky tvoří všechny příkazy pod záhlavím podmínky se stejným odsazením.

Tento typ toku programu se nazývá smyčka, protože se opakovaně vracíme k počátku. Uvědomme si, že když je podmínka nepravdivá již při prvním běhu smyčkou, příkazy uvnitř smyčky se nikdy neprovedou.

Tělo smyčky by mělo měnit hodnotu jedné či více proměnných tak, aby se podmínka nakonec stala nepravdivou a smyčka skončila. Jinak by se opakovala do nekonečna, a byla by to nekonečná smyčka.

V případě funkce countdown(n) můžeme dokázat, že smyčka končí, protože víme, že hodnota n je konečná a že se při každém průchodu smyčkou zmenšuje, takže nakonec se musíme dostat k nule. V jiných případech to tak zřejmé být nemusí.

def sequence(n):
    while n != 1:
        print(n, end=", ")
        if n%2 == 0:             # n je sudé  
            n = n//2
        else:              
            n = n*3+1
>>> sequence(4)
4, 2,
>>> sequence(3)
3, 10, 5, 16, 8, 4, 2, 

Podmínkou pro tuto smyčku je n != 1, takže bude rotovat tak dlouho, dokud nebude n=1, čímž se stane podmínka nepravdivá.

Při každém průchodu smyčkou program vrácí hodnotu n a zkontroluje, je-li sudá či lichá. Je-li sudá, je n děleno 2. Je-li lichá, nahradí se hodnotou n*3+1 - viz "sequence(3)".

Protože se n někdy zvětšuje, jindy zmenšuje, nemáme zřejmý důkaz, že n někdy dospěje k 1, nebo že program skončí. Pro jisté hodnoty n můžeme ukončení dokázat. Například, bude-li počáteční hodnotou mocnina 2, bude n stále sudé, dokud nedospěje k 1. V předchozím případě výsledná posloupnost takovou řadu (počínající číslem 16) obsahuje.

Odhlédneme-li od zmíněných čísel, je na místě otázka, zda umíme dokázat, že tento program je konečný pro všechny možné hodnoty n.   Zatím se to nikomu nepodařilo!

Jiným příkladem použití smyčky while je traverzování po elementech seznamu:

trampol.py                   # Výběr slov se sudým pořadím

slova = ["Copak", "je", "to", "za", "fešáka", "?"]

i = 0
délka = len(slova) 
while i < délka :
    if i % 2 == 1 :
        print(slova[i], end=", ") 
    i += 1
>>> %Run trampol.py
je, za, ?, 

Následující ukázka demonstruje použití pojmenovaného přiřazení (walruss operator) v kombinaci s podmínkou while a vstupem uživatele:

while (user_input := input('Enter q or p: ')) != 'q':
    if user_input == 'p':
        print("Hello!")
else: print("Quit!")              # jeden blok kódu
>>> Enter q or p: p
Hello!
>>> Enter q or p: q
Quit!
>>>

5.5   Podmínky if.. elif.. else

K napsání užitečného programu potřebujeme mít možnost měnit chování programu v závislosti na splnění či nesplnění jistých podmínek.
Tyto podmínky se formulují pomocí relačních (kap. 2.6.5) a logických (kap. 2.6.6) operátorů.

5.5.1 Podmínka if..

Tuto jednoduchou podmínku je vhodné použít jako součást jiné procedury, například funkce:

def odmocnina(x):
   if x >= 0:
      print ("odmocnina", x, "=", x**0.5)   

Stejně jako funkce a jiné složené příkazy se podmínka if skládá ze záhlaví a těla. Záhlaví začíná klíčovým slovem if, pokračuje booleovským výrazem neboli podmínkou a končí dvojtečkou.
Poté, co se podmínka vyhodnotí jako pravdivá, provede se odsazená část složeného příkazu.

>>> odmocnina(5)
odmocnina 5 = 2.23606797749979

Varianta s úpravou desetinného čísla pomocí f-stringu (viz 6.8.3):

def odmocnina(x):
   if x >= 0:
      y = x**0.5
      print (f"odmocnina {x} = {y:.2f}")
>>> odmocnina(5)
odmocnina 5 = 2.24

5.5.2 Podmínka if .. else

Touto podmínkou realizujeme takzvané alternativní provedení, při němž existují dvě možnosti a podmínka určí, která z nich se provede:

x = int(input("Zadej celé číslo: "))   # ošetření vstupu z konzoly

if x % 2 == 0:                  # při splnění podmínky
    print (x, "je sudé")        # se provede odsazený příkaz
else:                           # při nesplnění podmínky
    print (x, "je liché")       # se provede tento příkaz 
>>> Zadej celé číslo: 15
15 je liché

Je-li zbytek po dělení dvěma roven nule, potom víme, že x je sudé a program tuto zprávu zobrazí. Není-li tato podmínka pravdivá, provede se druhá sada příkazů. Protože podmínka může být pouze pravdivá nebo nepravdivá, provede se určitě jedna z obou možností.

Alternativám říkáme větve protože jsou rozvětvením toku programu.

Mimochodem, kdybychom potřebovali často posuzovat "lichost" či "sudost" čísel, mohli bychom si tento prográmek "zabalit" do funkce:
def parita(x):
    if x%2 == 0:                    # % je operátor modulo
        print(x, "je sudé")
    else:
        print(x, "je liché")

Podmínku if .. else lze také uplatnit jako infix mezi dvěma výstupy:

def parita(x):
    print(x, " je sudé") if x%2 == 0 else print(x, " je liché")    

Pro každou hodnotu x vrátí parita příslušnou zprávu. Při volání můžeme zadat jako argument jakýkoliv celočíselný výraz:

>>> parita(41)
41 je liché
>>> y = 41; parita(y + 1)
42 je sudé

5.5.3 Podmínka if .. elif .. else

Někdy jsou více než dvě možnosti a my potřebujeme více než dvě větve. Použijeme seriově uspořádané (zřetězené) podmínky:

def compare(x,y):
    if x < y:
       print(x, "is less than", y)
    elif x > y:
       print(x, "is greater than", y)
    else:
       print(x, " and ", y, " are equal")

elif je zkratka "else if". Opět je provedena pouze jedna větev. Počet elif není omezen, příkaz else však smí být pouze jeden a musí být uveden jako poslední.

Podmínky jsou zkoumány jedna za druhou. Je-li první nepravdivá, prověřuje se další. Je-li některá pravdivá, provede se bezprostředně následující větev programu. Jestliže je pravdivých podmínek více, provede se jen první z nich.

5.5.4 Procedura 'match' a 'case'

Procedura match a case imituje v jiných jazycích známou proceduru switch-case. Tyto obě procedury nahrazují sekvenci podmínek if.., elif.., elif.., else, popsanou v předchozím odstavci.

Skladbu této procedury je nejlépe ukázat na konkretním příkladě:

def věk_skupiny(age):
    match age:
        case _ if age < 0:
            print("Neplatný údaj")
        case _ if age <= 13:
            print("Dítě")
        case _ if age < 19:
            print("Mladistvý")
        case _ if age < 65:
            print("Dospělý")
        case _:
            print("Senior")

Poznámka: Podtržítka lze nahradit argumentem 'age'.

>>> print(věk_skupiny(25))
 Dospělý

5.5.5   Vnořené podmínky

Jedna podmínka může být vnořena do druhé. Předchozí trojité větvení v odstavci 5.5.3 můžeme realizovat také takto:

def compare_imb(x,y):
    if x == y:
        print(x, "and", y, "are equal")
    else:
        if x < y:
            print(x, "is less than", y)
        else:           
            print(x, "is greater than", y)
>>> compare_imb(9,12)
 9 is less than 12

Vnější podmínka má dvě větve. První větev obsahuje prostý příkaz k tisku. Druhá větev obsahuje další podmínku if, která má své vlastní dvě větve. Obě tyto větve jsou prostým voláním funkce print.

I když odsazení příkazů činí strukturu zápisu zřejmou, vnořené podmínky se velmi rychle stávají méně přehledné. Pokud můžeme, tak se jim raději vyhneme.

Vnořené podmínky můžeme často zjednodušit logickými operátory. Následující kód bude v další ukázce přepsán pro použití jen jedné podmínky:

def podm1(x):
    if 0 < x:
        if x < 10:
            print("x is a positive single digit.")

K volání funkce print dojde pouze tehdy, splníme-li obě podmínky (0 < x < 10).

def podm2(x):
    if 0 < x < 10:
        print("x is a positive single digit.")

5.5.6   Příkaz break

Při splnění podmínky ukončí příkaz break provádění smyčky. Tento příkaz lze použít u smyček for  i  while:

def finito(x):
    for letter in "Python":
        if letter == x:
            break
        print("Current Letter: ", letter)
finito('h')
 Current Letter:  P
 Current Letter:  y
 Current Letter:  t

nebo:

var = 8
while var > 0:
    print("Current var. value: ", var)
    var = var - 1  
    if var == 5:
        print("Adieu, var ==", var)
        break                # ukončí provádění smyčky     		
Current var. value:  8
Current var. value:  7
Current var. value:  6
Adieu, var == 5

5.5.7   Přikaz continue

Při splnění podmínky přeruší příkaz continue provádění smyčky a její výpočet pokračuje dalším elementem sekvence. Tento příkaz lze použít u smyček for i while:

def contin(x):
    for letter in "Python":
        if letter == "h":
            print("        Jsme přerušeni!")
            continue
        print("Current Letter: ", letter)  
contin("h")
 Current Letter:  P
 Current Letter:  y
 Current Letter:  t
         Jsme přerušeni!
 Current Letter:  o
 Current Letter:  n

nebo:

var = 6
while var > 0:
    var = var - 1   
    if var == 3:
        print("        Jsme přerušeni!")
        continue
    print("Current var. value: ", var)
print("Adieu!")
Current var. value:  5
Current var. value:  4
        Jsme přerušeni!
Current var. value:  2
Current var. value:  1
Current var. value:  0
Adieu!

5.6 Funkce len()

Touto často používanou vestavěnou funkcí zjistíme počet prvků iterovatelného objektu ze skupiny sekvence (string, list, tuple, range, bytes, bytearray, memoryview) a kolekce (set, frozenset, dict) - příkladmo:

>>> sg = "halelujah"                   # typ string
>>> len(sg)
 9

>>> bts = b'R\xc5\xaf\xc5\xbee'        # typ bytes
>>> len(bts)
 6

5.7 Funkce range

Vestavěná funkce

   range(n)                  # jen koncová mez       
   range(m, n)               # (start, stop)
   range(m, n, p)            # (start, stop, krok)

vytvoří potenciální číselnou posloupnost od 0 po n-1, případně od m po n-1, případně od m po n-1 s krokem p. Argumenty m a p jsou nepovinné.
Slovo potenciální označuje tu skutečnost, že místo celé zadané řady (celých) čísel se při deklaraci uloží pouze její parametry a k vlastnímu vytvoření této posloupnosti dojde až při jejím volání. Jedná se o případ takzvaného líného (lazy) vytvoření iterovatelného objektu.
Předností funkce range() je skutečnost, že zabírá stejné místo v paměti - bez ohledu na délku deklarované řady čísel.

Tuto posloupnost lze použít jako argument při tvorbě prostého seznamu, entice, setu a sekvence bytes:

>>> list(range (7))              # chápáno jako range(0,7)
[0, 1, 2, 3, 4, 5, 6]
>>> tuple(range (2,7))           # tuple je naše entice
(2, 3, 4, 5, 6)
>>> set(range (0,7,2))
{0, 2, 4, 6}
>>> bytes(range (0,7,2))        
b'\x00\x02\x04\x06'
>>> list(range (7,5))            # bez vhodného kroku vždy prázdný
[]
>>> list(range (7,0,-3))         # vida
[7, 4, 1]

Obrácenou posloupnost lze vytvořit i bez obrácených mezí a záporného kroku pomocí funkce reversed(). Tento postup nechodí pro objekt typu set a frozenset.

>>> list(reversed(range(7)))
[6, 5, 4, 3, 2, 1, 0]
>>> list(reversed(range(0,7,2)))
[6, 4, 2, 0]

Objekt typu range je indexovatelným (subscriptable) objektem. Jeho elementy jsou individuálně přístupné uvedením indexu v hranaté závorce i metodou __getitem__():

>>> range(7)[2]
2
>>> range(7).__getitem__(2)
2

Iterátor lze z objektu range vytvořit běžným způsobem funkcí iter(range), range.__iter__() nebo smyčkou for..., která si iterátor vytvoří sama (sama si jej však také zkonzumuje). Iterace takovéhoto iterátoru je dychtivá (eager).

>>> for i in range(7):                # eager iteration
        print(i, end= ", ")
0, 1, 2, 3, 4, 5, 6,
>>> for number in range(20):          # eager iteration
        if number%3 == 0:
            print(number, end="  ")
 0  3  6  9  12  15  18			

5.8 Funkce enumerate

Volání funkce enumerate(iterable, start=0) vrací objekt typu enumerate jenž je iterátorem, disponujícím metodou __next__() a funkcí next(). Parametr start je nepovinný, 0 je jeho implicitní hodnotou.

>>> fruits = ('banana','apple','pear','grape')  # iterábl
>>> enum = enumerate (fruits)                   # iterátor
>>> type(enum)                   # --> <class 'enumerate'>

Objekt typu enumerate není indexovatelný (není to iterábl), jeho elementy nejsou přímo přístupné prostřednictvím indexu v hranatých závorkách.

>>> enum[1]
# TypeError: 'enumerate' object is not subscriptable

Přístup k prvkům iterátoru lze realizovat postupně prostřednictvím iterační funkce next nebo smyčky for::

>>> next(enum)
(0, 'banana')
>>> enum.__next__()
(1, 'apple')

>>> for index in enum:
        print(index, end=" ")
(2, 'pear') (3, 'grape')                  # zbytek iterace	

Jak vidno, next(enum) i smyčka for vrací index a jemu příslušnou hodnotu. Index implicitně počíná nulou. Počáteční index lze změnit zadáním jiné hodnoty pro parametr start.

Funkci enumerate lze použít pro vyjádření vztahu argument - upravený argument:

>>> numbers = [2, 3, 4, 5]
>>> for index, value in enumerate (numbers):     
        print(numbers[index], " -> ", value**2)

 2  ->  4
 3  ->  9
 4  ->  16
 5  ->  25

5.9 Funkce filter, map a zip

Všechny tyto tři funkce (vyššího řádu) pracují s iterovatelnými objekty (iterábly - viz 3.1) a produkují iterátory.

Funkce filter(function, iterable) extrahuje ty prvky ze zadaného iteráblu (list, string, tuple, set, ...), pro něž zadaná funkce s podmínkou (predicate) vrací hodnotu True. Jako funkce s predikátem se často používá anonymní funkce lambda.

Funkce filter() vrací iterátor, jenž musíme vhodným způsobem rozbalit, například funkcí list(), tuple(), set():

>>> ages = [7, 10, 16, 18, 26, 35]            # iterábl
>>> adults = filter(lambda x: x > 17, ages))  # iterátor
>>> print("Adults: ", list(adults))     # nutno rozbalit
Adults:  [18, 26, 35]

# Případně iterátor rozbalíme hned:
>>> not_adults = tuple(filter(lambda x: x <= 17, ages))
>>> print("Not adults: ", not_adults)
Not adults:  (7, 10, 16)

Iterátor lze rozbalit i smyčkou for ... :

>>> for i in filter(lambda x: x % 2 == 0, ages):
        print(i, end=" ")
 10 16 18 26	

Pro přetvoření iteráblu lze použít i formát predikátové funkce:

>>> ages = [7, 10, 16, 18, 26, 35]
>>> def is_even(x):               # predikát
    return x % 2 == 0             # vrací True nebo False
	
>>> print(set(filter(is_even, ages)), end='')
 {10, 16, 18, 26}	

Argumentem pro parametr x je zde element seznamu ages.

Funkce map() aplikuje zadanou transformační funkci na každý element zadaného iteráblu (seznamu, entice, atp) a vrací sekvenci výsledků jako objekt map, což je iterátor, který nutno rozbalit (zde funkcí list).

>>> numbers = 1, 2, 3, 4             # iterábl
>>> def squares(n):                  # transformační funkce
        return n*n
>>> result = map(squares, numbers); result
 <map object at 0x000001AE79FA3460>  # objekt, který nutno rozbalit
>>> result = list(map(l(squares, numbers)); result
 [1, 4, 9, 16]                       # objekt již rozbalený

Jiný příklad s funkcí lambda:

>>> num1 = [4, 5, 6]      # Iteráblů lze zadat více než jeden.  
>>> num2 = [5, 6, 7]
# Zřetězení funkcí list() a map():
>>> result = list(map(lambda x,y: x+y, num1, num2)); result  # iterátor
[9, 11, 13]

Rozbalení stringů do entic použitím funkce list() a tuple():

>>> tup = 'šach', 'mat', 'pat'          # iterábl
>>> test = list(map(tuple, tup))        # rozbalený iterátor
>>> print(test)
 [('š', 'a', 'c', 'h'), ('m', 'a', 't'), ('p', 'a', 't')]

Funkce map() je napsaná v jazyce C. Obsahuje interní smyčku for ... s menším nárokem na potřebu paměti než tatáž smyčka, psaná v Pythonu.

Funkce zip(iterables) přijímá dvě či více iterovatelných sekvencí (iteráblů) a vytváří interní objekt typu zip, v němž jsou zadané sekvence agregované do jediné sekvence entic iterátoru.

>>> izip = zip([1, 2, 3], "postel", range(6)); izip   
 <zip object at 0x000001E753183380>            # iterátor
>>> list(izip)                                   
 [(1, 'p', 0), (2, 'o', 1), (3, 's', 2)]       # seznam

# Iterátor je prázdný, nutno jej znovu deklarovat:
>>> izip = zip([1, 2, 3], "postel", range(1,5))
>>> set(izip)                                
 {(3, 's', 2), (2, 'o', 1), (1, 'p', 0)}        # set
                                
# nebo sloučit deklaraci sekvence s deklarací iterátoru:
>>> tuple(zip([1, 2, 3], "postel", range(6)))      
 ((1, 'p', 0), (2, 'o', 1), (3, 's', 2))        # entice    
>>> dict(zip([1, 2, 3, 4], "postel"))        
 {1: 'p', 2: 'o', 3: 's', 4: 't'}               # slovník

Počet entic ve výsledné sekvenci je dán počtem argumentů funkce zip. Délka výsledných entic je dána délkou nejkratšího argumentu. U slovníku mohou být pouze dva argumenty. Jeho enticemi jsou dvojice klíč:hodnota.

Od verze Python 3.10 existuje párový argument strict=True, který zkontroluje délku vstupních argumentů a v případě neshody hlásí chybu:

>>> set(zip([1, 2, 3, 4], "postel", strict=True))
ValueError: zip() argument 2 is longer than argument 1

Je nicméně možné vytvořit iterátor podle nejdelší vstupní sekvence a to pomocí funkce zip_longest() z modulu itertools:

>>> from itertools import zip_longest

>>> numb = [1, 2, 3]
>>> lett = ['a', 'b', 'c']
>>> long = range(5)
>>> izip = zip_longest(numb, lett, long, fillvalue='#')
>>> list(izip)
 [(1, 'a', 0), (2, 'b', 1), (3, 'c', 2), ('#', '#', 3), ('#', '#', 4)]
Souhrnná poznámka

Výstupem z funkce filter(), map(), zip() je iterátor a lze jím procházet (traverzovat) funkcí iter() nebo smyčkou for ... :

>>> izip = zip([1, 2, 3], "postel", range(6))
>>> next(izip)
 (1, 'p', 0)
>>> for i in izip:
        print(i)
 (2, 'o', 1)
 (3, 's', 2)
>>> next(izip)                   # iterátor je prázdný 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration                      
# Prázdný iterátor zaplníme opětovným voláním proměnné izip.

Interní objekt iterátoru lze také rozbalit funkcí list(), tuple(), set() a dict.


5.10 Funkce  reduce

Funkce vyššího řádu reduce(fce, iterable, initial) vrací jedinou výslednou hodnotu z postupné aplikace zadané funkce pro dvě po sobě zadané hodnoty v sekvenci. Parametr initial je nepovinný. Tuto funkci nutno importovat z modulu functools.

>>> from functools import reduce
>>> seqv = 1,2,3,4

# S funkcí pro dva argumenty: 
>>> def do_sum(x, y):          
       return x + y
>>> reduce(do_sum, seqv)                      --> 10

Případně s funkcí lambda:
>>> reduce(lambda x,y: x+y , range(1,5))      --> 10

Případně s hodnotou pro parametr initial:
>>> reduce(do_sum, seqv, 12)                  --> 22

Funkce reduce() zde postupně provádí součty (((1+2)+3)+4), eventuelně (((12 (argument pro parametr initial) +1)+2)+3)+4).
Hodnota parametru 'initial' se při realizaci funkce reduce() zařadí na první pozici zpracovávané sekvence. Datové typy argumentů pro sekvenci i parametr 'initial' musejí být shodné.
Parametr initial je důležitý pro situaci, kdy by argument pro parametr iterable měl být prázdný. V tom případě by funkce reduce() bez argumentu pro initial skončíla sdělením TypeError.

Inteligentní náhradou invokace reduce(do_sum, seqv) je funkce sum(num_seqv), kde num_seqv je řada čísel - s tím rozdílem, že sum() neumí slučovat řetězce jako funkce reduce():

>>> sum(seqv)
10
Pro sumaci řetězců (konkatenaci) lze použít tento výraz:
>>> from functools import reduce
>>> faq = "pes", "kočka"
>>> reduce(lambda x,y: x+y, faq, "sumace = ")
'sumace = peskočka'

5.11 Newtonova metoda

Smyčky jsou často užívány v programech, které spočítají výsledek tak, že začnou s přibližnou hodnotou a opakovaným výpočtem výsledek zpřesňují.

Například, jedním ze způsobů počítání druhé odmocniny čísla je Newtonova metoda. Při určení odmocniny čísla n začneme v podstatě libovolnou aproximací (přibližnou hodnotou), kterou zpřesníme pomocí tohoto vzorce:

better = (approx + n/approx) / 2

Výpočet se opakuje tak dlouho, až se zpřesněná hodnota téměř neliší od předchozí. Pro provádění výpočtu napíšeme funkci:

def sqrt(n):
    approx = n/2.0
    better = (approx + n/approx)/2.0
    while better != approx:
        approx = better
        better = (approx + n/approx)/2.0  
    return approx

Volejme funkci pro argument 25 , abychom se přesvědčili, že výsledek je 5 .

Newtonova metoda je příkladem algoritmu - obecného řešení určitého problému (v našem případě počítání druhé odmocniny).


5.12 Cvičení

Napište definice funkcí s invokacemi v zadaných doctestech. Všechny příklady zapisujte do samostatných souborů ve složce Kap-05.

Příklady s doctestem budou mít na konci skriptu tento kód:

if __name__ == '__main__':
    import doctest
    doctest.testmod()

Tyto příklady řešte až po prostudování textu v kap. 3.10.

  1. Napište funkci compare(a,b), která vrací 1 pro a>b, 0 pro a==b a -1 pro a<b.
    def compare (a, b):
        """
        >>> compare(5,4)
        1
        >>> compare(7,7)
        0
        >>> compare(2,3)
        -1
        """
        # Zde napište tělo funkce
    
  2. Napište funkci hypo, která vrací délku přepony pravoúhlého trojúhelníka pro zadané délky odvěsen.
    def hypo (a,b):
        """
        >>> hypo(3,4)
        5.0
        >>> hypo(12,5)
        13.0
        >>> hypo(7,24)
        25.0
        """
        # tělo funkce může mít pouhé 2 řádky
    
  3. Napište funkci slope(x1,y1,x2,y2), která vrací tangent úhlu (sklonu) přímky procházející body (x1,y1) a (x2,y2). Funkce musí vyhovět následujícím testům:
    def slope (x1,y1,x2,y2):
        """
        >>> slope(5,3,4,2)
        1.0
        >>> slope(1,2,3,2)
        0.0
        >>> slope(1,4,1,2)         
        kolmice k ose 'x'           Ošetřená výjimka!
        >>> slope(2,4,1,2)
        2.0
        """
        # Zde napište tělo funkce
    
  4. Napište funkci intercept(x1,y1,x2,y2), která vrací ypsilonovou pořadnici průsečíku přímky s osou y (pro x=0). Použijte rovnici přímky, procházející 2 body.

    def intercept (x1,y1,x2,y2):
        """
        >>> intercept(1,6,3,12)
        3.0
        >>> intercept(6,1,1,6)
        7.0
        >>> intercept(4,6,12,8)
        5.0
        """
        # Zde napište tělo funkce
    
  5. Napište funkci is_multiple(m,n), která vrátí True, je-li m násobkem n a False, není-li.
    def is_multiple( (m,n):
        """
        >>> is_multiple(12,3)
        True
        >>> is_multiple(12,4)
        True
        >>> is_multiple(12,5)
        False
        """
        # Zde napište tělo funkce
    
  6. Napište fci is_divisible(m,n), která přijme celá čísla jako argument a podle situace vytiskne True nebo False.

    def is_divisible( (m,n):
        """
        >>> is_divisible(20,4)
        True
        >>> is_divisible(21,8)
        False
        """
        # Zde napište tělo funkce
    

    Při řešení použijete podmínky if ... :, else: a vyberete si jeden z existujících způsobů dělení: normální a/b, celočíselné a//b nebo dělení se zbytkem (modulo) a%b.

  7. Napište tělo funkce fah2cel(t), která převede teplotu ve stupních Fahrenheitových na stupně Celsiovy a vrátí ji jako celé číslo. Použijete vestavěnou fci round(n). Informaci o této funkci získáte zadáním round.__doc__ v konzole Pythonu.
    def fah2cel(t):
        """
        >>> fah2cel(212)
        100
        >>> fah2cel(32)
        0
        >>> fah2cel(-40)
        -40
        >>> fah2cel(37)
        3
        """
        # Zde napište tělo funkce
    
  8. Napište funkci cels2fah(t), která převede teplotu ve stupních Celsiových na stupně Fahrenheitovy.

pre up next title end end