Počítač zpracovává data hromadně

Z Základy informatiky pro střední školy
Přejít na: navigace, hledání

Počítač to vidí jinak: zjišťování dělitelnosti · Počítač někdy nedělá, co chci

ikona boxu
Co se naučíš:
  • Jak pomocí seznamů uchovávat celé posloupnosti hodnot, tzv. seznamy, a potom pracovat s celými seznamy či jejich jednotlivými prvky.
  • Využít for cyklus k automatickému zpracování všech prvků daného seznamu.
  • Zpracovávat celé rozsahy čísel a určovat počet opakování cyklu s pomocí funkce range.

Uchování několika hodnot v jedné proměnné: datový typ seznam

ikona boxu
K čemu dělitele?

Znát dělitele čísla se hodí k určení prvočíselnosti, ale také ke hledání dokonalých nebo spřátelených čísel. Třeba takové liché dokonalé číslo zatím nikdo nenašel.

Vypsat dělitele daného čísla na výstup je pěkné, ale neumožňuje je to dále využít. Mohli bychom je samozřejmě uložit do proměnných. Potíž je, že předem nevíme, kolik jich kdy bude. Nemá proto smysl zavádět proměnné Delitel1, Delitel2, Delitel3 atd. Přitom je ale nepřeberně situací, kdy bychom rádi pracovali se skupinou souvisejících hodnot.

ikona boxu
Další složené datové typy

Seznam (typ list) je zcela základní informatická struktura. Pracovat jen se seznamy by ale bylo často otravné. Python umí pracovat také s množinami (typ set) či tzv. slovníky (typ dict). Zcela zásadní strukturou jsou objekty. Ze takových struktur potom programátoři tvoří další, např. stromy a další grafy.

Taková přání nám splní seznamy. Seznam je složený datový typ. Umožňuje pracovat s více hodnotami uspořádanými za sebou, tedy seznamem:

  • seznam známek z fyziky
  • seznam cestujících letadla
  • nákupní seznam
  • posloupnost naměřených teplot
  • seznam barev jednotlivých pixelů bitmapového obrázku
  • seznam hraničních hodnot pásem BMI


ikona boxu
Úloha: Základní použití seznamů

Vyzkoušej následující kód:

Zkus jej různě pozměnit a sleduj, jak se mění výsledky.

Některé obraty už znáš z práce s textovými řetězci, které jsou zvláštní variantou seznamu.

Kontrolní otázka: Jak zjistit index posledního prvku v seznamu?

Řešení

Index posledního prvku je o jedna menší, než počet prvků seznamu:

len(Seznam)-1

Prověř to!


Pečlivě si přepočítej, jestli je NakupniSeznam[2] opravdu druhá položka seznamu. S počítáním od nuly jsme se už setkali v kapitole o informaci. Je to jednak praktické a jednak běžnější, než si možná myslíš. Když se narodíš, tak ti v prvním roce života není jeden rok. První řád pozičních číselných soustav odpovídá nulté mocnině základuTedy jednotkám, nikoliv desítkám nebo třeba dvojkám, což by byly mocniny první. První, do čeho v budově vstoupíš, obvykle není první patro. První hodina po půlnoci nemá číslo jedna.

Naopak v našem letopočtu není rok nula. Prvním rokem byl tedy rok jedna, stým rokem rok 100, druhé století začalo rokem 101 a třetí tisíceletí rokem 2001. Což mate leckoho, kdo ví, že nula je taky číslo.

ikona boxu
Úloha: Operátor +

Co dělá operátor +? Vyzkoušej následující kód:

Proč nestačí napsat ProdlouzenyVikend = "pa" + Vikend, bez hranatých závorek? Jestli nevíš, tak to zkus!


Řešení

Operátor + spojují dva seznamy do jednoho. Speciálním případem je rozšíření seznamu o jediný prvek, tedy spojení s jednoprvkovým seznamem.

ikona boxu
Lepší možnosti

Uvedené příklady jsou jednoduché, ale poněkud neefektivní. Obvykle vytváří nové kopie původních seznamů, místo aby prostě upravily již existující seznamy. Když chtějí jeden seznam prodloužit (nikoliv vytvořit nový a delší) profesionální programátoři, použijí zápis ProdluzovanySeznam.extend( PridanySeznam ). To na konec seznamu ProdluzovanySeznam připojí PridanySeznam. Je to tedy podobné jako zápis ProdluzovanySeznam = ProdluzovanySeznam + PridanySeznam, který oba seznamy spojí a výsledek přiřadí do seznamu ProdluzovanySeznam namísto toho původního.

Podobně, pokud chtějí pouze jeden prvek přidat, zadají ProdluzovanySeznam.append( NovyPrvek ). Zde si tolika příkazy nechceme zatěžovat hlavu, ale mezi programátory jsou metody extend a append samozřejmě využívanější, protože se snáze čtou a pracují rychleji.

ikona boxu
Další možnosti

Protože se se seznamy v Pythonu pracuje velmi často, existuje řada dalších možností, které už jsou připraveny a není třeba je programovat. Vyzkoušej např. tyto zápisy a zjisti, k čemu slouží:

DnyVTydnu = PracovniDny + Vikend
PredposledniDen = DnyVTydnu[-2]
Vikend = DnyVTydnu[5:7]
LicheDny = DnyVTydnu[::2]
del DnyVTydnu[0]    # nemate radi pondeli?

Když budeš potřebovat, najdi si pomoc na internetu!


Příklad: Úprava hledání dělitelů

Upravíme již hotovou funkci, která seznam dělitelů vypisuje, aby jej vrátila k dalšími použití. Úprava není nic náročného. Namísto vytištění hodnoty ji přidáme na konec seznamu dělitelů. Na začátku bude seznam prázdný, a na konci ho naplněný vrátíme.

def Delitele (Cislo) :
    SeznamDelitelu = []			# na zacatku mame prazdny seznam delitelu
    Delitel = 1
    while Delitel <= Cislo :
        if Cislo % Delitel == 0 :				# nasli jsme dalsiho delitele?
            SeznamDelitelu = SeznamDelitelu + [Delitel] 	# pridame ho do seznamu
            Delitel = Delitel + 1
    return SeznamDelitelu			# vysledny seznam vratime
# puvodni zdrojovy kod
def VypisDelitele (Cislo) :
    Delitel = 1
    while Delitel <= Cislo :
        if Cislo % Delitel == 0 :
            print (Delitel)
        Delitel = Delitel + 1

Porovnej uvedený zdrojový kód s tím, který dělitele tiskne. Čím se liší, a co mají společného?

ikona boxu
Úloha: Hledání prvočísel

Předchozí funkci můžeme využít např. k ověření, jestli je dané číslo prvočíslem. Taková informace se velmi hodí v šifrováníNáš jednoduchý program by byl ale pro takové účely zoufale pomalý.. Abychom mohli zjišťovat, jestli je nějaké číslo prvočíslem, musíme vědět, co to znamená. Zformuluj nebo najdi definici prvočísla, kterou budeme moci použít pro počítačové ověření prvočíselnosti.

Řešení

Jedna z možných definic říká, že prvočísla jsou právě čísla s právěneboli přesně dvěma děliteli.

Definici prvočíselnosti tedy s pomocí předchozí funkce na nalezení dělitelů umíme ověřit velmi lehce! Sestav program na určení prvočíselnosti. Pokud je číslo v parametru prvočíslem, vrátí True, pokud ne, vrátí False.

Řešení

Řešení:

def JePrvocislem (Cislo) :
    PocetDelitelu = len(Delitele(Cislo))	# nejdriv spocteme pocet delitelu
    if PocetDelitelu == 2 :			# pokud jsou dva, je Cislo prvocislem
        return True
    else :					# jinak prvocislem neni
        return False

Ověř, zda řešení funguje!

Příklad: Ještě jednou pásma BMI

BMI už umíme spočítat. Umíme také namísto nicneříkajícího čísla vypsat název pásma. Není to ale zrovna elegantní. Navíc když se tabulkové hodnoty změní, je nutné změnit program. Tomu se snažíme vyhnout. Když se změní jen data, ale nikoliv logika jejich zpracování, nemělo by být nutné do žádného programu vůbec zasahovat. Tabulku si ale můžeme zaznamenat např.Existuje mnoho dalších (i lepších) způsobů, pro nás je zde teď rozhodující, že se nemusíme hned učit něco nového. pomocí dvou seznamů — jednoho pro meze pásem, druhého pro jejich názvy. To nám dovolí napsat program elegantněji, nikoliv jako sérii rozhodování.

ikona boxu
Chyba v zadání

V testovací výstupech je zaměněna výška a váha. Program funguje správně, ale pro nesmyslná data samozřejmě poskytne nesmyslné výsledky. Chybu lze odhalit pozornou kontrolou, nebo třeba doplněním výpisu hodnoty BMI.

Uvedený příklad vyzkoušej, prozkoumej a doplň komentáře, kde uznáš za vhodné. Dovedeš vysvětlit, jak a proč program funguje? Odpovídají testovací výstupy tvému očekávání? Proč?


Lidská populace se vyvíjí a přibývá na váze. Možná se dočkáme změny v tabulce pásem — mohou se posunout hranice pásem, nebo třeba nové pásmo přibude. V takovém případě bude stačit upravit jen hodnoty v našem seznamu a není nutné zasahovat do fungování programu.

for cyklus: pro každý prvek seznamu opakuj...

Vraťme se k příkladu nákupního seznamu. Když ho vytiskneme pomocí funkce print (a potom na tiskárně), výsledek při nákupu moc nevyužijeme. Lepší by bylo vytisknout každou položku na jeden řádek a umožnit zaškrtávání.

ikona boxu
Úloha: Zaškrtávací seznam

Naprogramuj funkci, která jako parametr dostane seznam a vytiskne jeho obsah po jedné položce na řádek spolu se zaškrtávacím políčkem — tak, aby byl výsledek užitečný při nákupu. Například NakupniSeznam = ["vejce", "slanina", "toustovy chleb"] se vytiskne jako:

[] vejce
[] slanina
[] toustovy chleb

Funkce musí umět pracovat se seznamy různých délek. Příjemné je, že jsou v seznamech prvky uspořádány a označeny indexy(pořadovými čísly). Můžeme tedy pracovat s proměnnou, jejíž hodnotu budeme v cyklu měnit od nuly do nejvyšší hodnoty indexu — podobně jako proměnnou Delitel v úloze na hledání dělitelů.

Kostru funkce jsme navrhli pomocí komentářů. Doplň mezi ně potřebný zdrojový kód (nebo vymysli a napiš svůj vlastní). Zkontroluj, jestli testovací výstupy odpovídají tvému očekávání.

ikona boxu
Nápověda

Program nemusíš psát od začátku do konce. Dobře si jej rozmysli a postupně vpisuj ty části kódu, které jsou jasné. Mohou se tím vyjasnit i další části. Pokud se přesto dostaneš do nesnází, pečlivě zformuluj, co přesně potřebuješ vědět.

  • Jak přimět Python, aby vytisknul to „zaškrtávací políčko“? To políčko jsou ve skutečnosti jen hranaté závorky: []. Tohle je jednoduchý příklad, pomocí základních znaků se ale dají tvořit i mnohem zajímavější díla (nápisy i obrázky) než jen zaškrtávací políčka a smajlíky.
  • Jak zajistit, aby Python vytisknul na tentýž řádek políčko a zároveň název zboží? Jinými slovy se jedná o dva textové řetězce, které je potřeba spojit. A to už umíš!
  • Jak postupně zpracovat prvky seznamu jeden za druhým? K jednotlivým prvkům přistupujeme přes jejich index. Bude tedy potřeba pracovat s indexem, který by měl postupně nabývat hodnoty od nuly až po index posledního prvku seznamu. S jednotlivými prvky děláme pořád to samé, takže asi bude vhodné využít cyklus.

Řešení

def ZaskrtavaciSeznam( Seznam ) :	# definice funkce ZaskrtavaciSeznam, parametrem je Seznam
    Index = 0				# zacneme prvnim prvkem, tedy tim s indexem 0
    while Index < len( Seznam) :		# dokud je index nizsi nez pocet prvku seznamu
        print ("[] " + Seznam[Index])		# vytiskni zaskrtavaci policko a prvek s aktualnim indexem
        Index = Index + 1			# zvys index o jedna
        # konec cyklu
    #hotovo

Řešení nezapomeň otestovat!

Opakování téhož úkolu se všemi prvky seznamu je v programování velmi časté. Neustále znovu vypisovat, jak se mění řídící proměnnáDelitel, Index atp. by nebylo efektivní. Lepší je využít cyklu for, který udělá to saméTady poněkud zjednodušujeme, ale pro naše účely je to akorát., ale stručněji a přehledněji:

ikona boxu
Nejen seznamy

Pomocí for cyklu lze procházet i další zmíněné struktury, např. množiny nebo slovníky.

Schválně tuhle variantu vyzkoušej! Porovnej funkci na tisk seznamu s cyklem while a s cyklem for.

Co tedy zápis for Prvek in Seznam přesnědělá? Postupně do proměnné Prvek dosazuje hodnoty ze seznamu Seznam, a s každou takovou hodnotou provede tělo cyklu. Odsazené řádky se tedy poprvé realizují s první hodnotou, potom znovu, ale s druhou hodnotou v seznamu, a tak dále, dokud není jednou použit každý prvek seznamu.


Příklad: Hádanka s více odpověďmi

Některé hádanky mají několik řešení, a i když mají jedno, lze obvykle vyjádřit různými způsoby. Náš původní program si s takovou situací poradit neumí. Možné odpovědi bychom ale mohli uložit do seznamu a tím problém vyřešit.

Jednotlivé správné odpovědi uložíme do seznamu SpravneOdpovedi. Potom zkontrolujeme, jestli je odpověď uživatele jedna ze správných možností. Budeme si to pamatovat ve zvláštní proměnné NasliJsme. Na začátku kontroly jsme zatím shodu nenašli, zadáme tedy hodnotu False. Potom budeme postupně procházet všechny správné odpovědi. Pokud najdeme shodu s odpovědí uživatele, poznamenáme si tento fakt nastavením hodnoty NasliJsme na True. Až cyklus prohledávání skončí, stačí se podívat na NasliJsme. Projdi si zdrojový kód krok za krokem, ať mu dobře porozumíš.

def Hadanka () :
    OdpovedUzivatele = input("ZADANI HADANKY")
    SpravneOdpovedi = ["Prvni moznost", "Druha moznost"]
    NasliJsme = False
    for SpravnaOdpoved in SpravneOdpovedi :
        if OdpovedUzivatele == SpravnaOdpoved :
            NasliJsme = True
    if NasliJsme :
        print("Spravne, dobra prace!")
    else :
        print("Je mi lito, to neni spravne.")

Příklad: Pohyby na bankovním účtu

Na bankovních účtech přibývají a ubývají peníze. Jeden ze způsobů kontroly správnosti bankovního výpisu je ověření, jestli pohyby vedou na správný konečný zůstatek. Pohyby jsou číselné hodnoty v pořadí za sebou, což si říká o použití seznamu (mj. proto, že nevíme předem, kolik pohybů vlastně kdy bude). Podívejme se na program, který umožní zůstatek spočítat (kdo nechce, tak se nedívá, a zkusí ho nejdřív vymyslet a naprogramovat).

Pro srovnání uvedeme opět dvě varianty, s cyklem while a s cyklem for. Můžeš si ušetřit práci a prozkoumat příklady ve dvojici: Vyber si jednu z variant, zjisti, jak přesně funguje a vysvětli to partnerovi.

Pohyby = [3540, -2100, -360, -522, 1400]
def Zustatek(PocatecniStav, Pohyby) :
    Stav = PocatecniStav
    PoradoveCislo = 0
    while PoradoveCislo < len(Pohyby) :
        Stav = Stav + Pohyby[PoradoveCislo]
        PoradoveCislo = PoradoveCislo + 1
    return Stav
Pohyby = [3540, -2100, -360, -522, 1400]
def Zustatek(PocatecniStav, Pohyby) :
    Stav = PocatecniStav
    for Pohyb in Pohyby :
        Stav = Stav + Pohyb
    return Stav

Jaké komentáře by bylo vhodné doplnit, aby byly programy srozumitelnější?

Opakování pro každé číslo od jedné do...

Při hledání dělitelů jsme systematicky zkoušeli všechny potenciální dělitele. S potřebou něco provést pro každé celé číslo z daného rozsahu se v programování setkáváme velice často. Python proto nabízí funkci range (podle anglického slova rozsah). Generuje celá čísla v rozsahu podle zadaných parametrů.

Příklad: Hledání dělitelů s cyklem for

Porovnej následující dva zdrojové kódy. Kód nalevo využívá for cyklus pro stejnou činnost, jakou provádí k napravo. Ten už znáš z příkladu výše.

def Delitele (Cislo) :
    SeznamDelitelu = []			 # na zacatku mame prazdny seznam delitelu
    for Delitel in range( 1, Cislo + 1) :
        if Cislo % Delitel == 0 :				# nasli jsme dalsiho delitele?
            SeznamDelitelu = SeznamDelitelu + [Delitel] 	# pridame ho do seznamu
    return SeznamDelitelu			# vysledny seznam vratime
def Delitele (Cislo) :
    SeznamDelitelu = []			# na zacatku mame prazdny seznam delitelu
    Delitel = 1
    while Delitel <= Cislo :
        if Cislo % Delitel == 0 :				# nasli jsme dalsiho delitele?
            SeznamDelitelu = SeznamDelitelu + [Delitel] 	# pridame ho do seznamu
            Delitel = Delitel + 1
    return SeznamDelitelu			# vysledny seznam vratime

Použití funkce range( 1, Cislo + 1) v příkladu zajišťuje, že cyklus zkusí všechny hodnoty od jedné do hodnoty Cislo. První parametr tedy udává počáteční hodnotu rozsahu. Druhý parametr pak udává hodnotu, která už není součástí daného rozsahu. Proto jsme museli v tomto případě přidat + 1, jinak by nám poslední dělitel chyběl.

ikona boxu
Úloha: Záludnosti funkce range

Využij třeba interaktivní konzole a prozkoumej možnosti parametrů pro funkci range. Mohou být záporné? Co když jeden chybí? Co když je jeden navíc? Může první převyšovat druhý? Lze vygenerovat klesající posloupnost?

Řešení

Pro úplnost uvedeme důležité odpovědi:

  • Parametry mohou být záporné, musí ovšem být celočíselné.
  • Tři parametry jsou začátek rozsahu, konec rozsahu (první hodnota mimo rozsah) a krok.
  • Jsou-li parametry dva, předpokládá Python, že chybí zadání kroku. Předpokládá proto jeho hodnotu 1, což je také nejčastěji potřebná hodnota. Výsledkem jsou „obyčejná čísla jdoucí za sebou“.
  • Je-li parametr jen jeden, předpokládá Python, že chybí i začátek rozsahu. Předpokládá proto jeho hodnotu 0, což je také nejčastěji potřebná hodnota. Výsledkem jsou tedy hodnoty 0, 1, 2, ....

V minulé kapitole jsme viděli příklad se stavebním robotem. Stavěl zeď z daného počtu řad a každá řada byla složená z daného počtu cihel. S využitím funkce range lze totéž zapsat přehledněji:

1 def PostavZed (CilovaVyska, CilovaSirka) :
2 	for AktualniVyska in range(CilovaVyska) :
3 		for AktualniSirka in range(CilovaSirka) :
4 			PrilozDoRadkuCihlu(AktualniVyska, AktualniSirka)
1 def PostavZed (CilovaVyska, CilovaSirka) :    # verze programu s cyklem while a "rucnim" pocitanim opakovani
2 	AktualniVyska = 0
3 	while AktualniVyska < CilovaVyska :	# tedy "dokud nemame dost vysokou zed"
4 		AktualniSirka = 0			# zacneme novy radek
5 		while AktualniSirka < CilovaSirka :	# dokud neni radek hotov
6 			PrilozDoRadkuCihlu(AktualniVyska, AktualniSirka)	# prilozime cihlu k cihle
7 			AktualniSirka = AktualniSirka + 1	# prilozenou cihlu pripocteme k sirce radku
8 		AktualniVyska = AktualniVyska + 1		# radek je hotov, zase mame o kus vyssi zed


Vidíme, že kombinaci for a range lze jednoduše využít k zadání přesného počtu opakování cyklu. Požadovaný počet opakování prostě zadáme jako parametr funkce range. Řídící proměnná navíc bude obsahovat pořadové čísloPočítané od nuly, samozřejmě. právě probíhajícího opakování.

Shrnutí

ikona boxu
  • K práci s posloupnostmi souvisejících hodnot využíváme datový typ seznam. Můžeme ho vytvořit výčtem hodnot oddělených čárkami v hranatých závorkách:
    Kodony = ["A", "C", "T", "G", "U"]
  • Ke konkrétnímu prvku seznamu se dostaneme pomocí jeho indexu (pořadového čísla) v hranatých závorkách (Kodony[2] je "T"). Pozor na to, že index prvního prvku je nula.
  • Dva seznamy lze spojit operátorem plus:
    VsechnyOhrozeneDruhy = KritickyOhrozeneDruhy + OhrozeneDruhy + ZranitelneDruhy
  • Stejný operátor lze v využít k přidání nového prvku (v podobě jednoprvkového seznamu) na začátek nebo konec seznamu:
    NovySeznam = ["Novy prvni prvek"] + StarySeznam + ["Novy posledni prvek"]
  • Délku seznamu (počet jeho prvků) zjišťujeme pomocí funkce len, platí tedy např. len(Kodony) == 5.


  • Chceme-li udělat postupně s každým jednotlivým prvkem seznamu totéž, použijeme cyklus for.
  • Zápis for Prvek in Seznam : bude opakovaně spouštět následující odsazené řádky (tělo cyklu), ovšem pokaždé vloží do proměnné Prvek jinou hodnotou ze seznamu Seznam.
  • I cyklus for můžeme podle potřeby kombinovat s dalšími prvky jazyka Python, například ho vnořit do rozhodování if (cyklus by se spustil jen za dané podmínky), nebo naopak uvnitř cyklu for mít další vnořený cyklus.


  • Chceme-li zpracovat celá čísla z nějakého rozsahu, použijeme:
    for Cislo in range(ZacatekRozsahu, KonecRozsahu, Krok)
  • Nejčastěji použijeme parametr jen jeden. Volání range(AzDoCisla) odpovídá volání range(0, AzDoCisla, 1). Ten jeden zadaný parametr je tedy chápán jako horní mez rozsahu.
  • Začátek rozsahu v nule a konec před zadanou horní mezí má užitečný důsledek. Rozsah range(PocetPrvku) ma přesně PocetPrvku prvků. V kombinaci s for cyklem tak lze dosáhnout přesně požadovaného počtu opakování.

Pro práci se seznamy (a dalšími strukturami) existuje v Pythonu mnoho dalších nástrojů, které programátorům šetří práci každý den. Seznámit se s nimi můžete v dokumentaci či libovolné učebnici jazyka Python.


Úlohy

ikona boxu
Úloha: Zestručnění hledání dělitelů

Funkci JePrvocislem možná dovedeš zapsat i úsporněji, než jak jsme ji viděli výše. Zkus to!

ikona boxu
Nápověda
  • Proměnnou PocetDelitelu nutně nepotřebuješ, je použita jen jednou, stačí na daném místě prostě jen spočíst její hodnotu.
  • Navracená logická hodnota přímo odpovídá podmínce, mohli bychom ji tedy přímo vracet a obešli bychom se úplně bez rozhodování (if).
  • Celkově tedy postačí jediný řádek zdrojového kódu.

Řešení

def JePrvocislem (Cislo) :
    return len(Delitele(Cislo)) == 2

Porovnej původní a zestručněnou verzi. Která ti vyhovuje lépe? Proč?

ikona boxu
Úloha: Zrychlení hledání dělitelů

Funkce Delitele dělá značné množství zbytečné práce. Podaří se ti ji vylepšit?

ikona boxu
Nápověda

Zkus funkci provést ručně s nějakým vetším číslem, třeba 69. Všímej si, jestli nemáš nutkání některé potenciální dělitele přeskočit i bez počítání, a jestli je to nutkání oprávněné. Když objevíš nějakou možnost systematického vylepšení, zahrň jej do programu. Nezapomeň jej otestovat, aby byla jistota, že se v něm úpravou neobjevila nová chyba.

Řešení

Někdo by si např. mohl všimnout, že stačí dělitele zkoušet do hodnoty Cislo/2. Žádný větší dělitel, kromě samotného čísla, jistě neexistuje. Zde uvedeme úpravu pro variantu s while cyklem, variantu s for cyklem podle ní jistě zvládneš upravit samostatně.

def Delitele (Cislo) :
    SeznamDelitelu = []			
    Delitel = 1
    while Delitel <= Cislo/2 :			# vetsi delitele nez Cislo/2 nejsou, nemusime je tedy zkouset
        if Cislo % Delitel == 0 :
            SeznamDelitelu = SeznamDelitelu + [Delitel] 
        Delitel = Delitel + 1
    SeznamDelitelu = SeznamDelitelu + [Cislo]	# nezapomeneme na samotne Cislo (musime na nej myslet zvlast, protoze ho nezpracujeme v ramci cyklu)
    return SeznamDelitelu

O kolik rychleji poběží tvoje vylepšená verze? Zadej pořádně velké číslo a vyzkoušej to! Uvědom si taky výhodu vzájemného použití funkcí: každá funkce, která teď budě využívat rychlejší verzi hledání dělitelů, sama poběží rychleji.

ikona boxu
Úloha: Hledání prvku v seznamu
ikona boxu
Zjišťování přítomnosti prvku v seznamu lépe a jednodušeji

Funkce JeVSeznamu je tak užitečná, že je do Pythonu přímo vestavěna. Zkus místo řádku

    if JeVSeznamu (OdpovedUzivatele, SpravneOdpovědi) :

zadat prostě

    if OdpovedUzivatele in SpravneOdpovědi :

Výraz Prvek in Seznam nabývá hodnoty True právě tehdy, když Seznam obsahuje Prvek.

Obecně se dá říct, že drtivá většina takto jednoduchých problémů už byla dávno vyřešena a velmi kvalitně naprogramována. Programátoři proto používají hotová a ověřená řešení (místo aby psali vlastní a riskovali chyby). My si zde ovšem rádi leccos naprogramujeme znovu. Jednak proto, abychom se pocvičili, a jednak proto, abychom věděli, jak to vlastně funguje „uvnitř“. Tady např. díky vlastnímu programu vidíme, že podmínku „in“ nelze vyhodnotit okamžitě, Python musí celý seznam projít a zkontrolovat jednotlivé hodnoty. Přestože ve zdrojovém kódu jde o jediný řádek, v případě dlouhých seznamů může jeho provedení zabrat dlouhou dobu.

Všimni si, že in se chová různě podle toho, jestli je v if nebo for! Zamysli se: co dělá cyklus while Prvek in Seznam : ..., za jakých podmínek, proč?

Zjišťování, jestli je daný prvek v daném seznamu je velmi častá úloha. Není efektivní programovat její řešení pokaždé znovu. Naprogramuj funkci, která vrátí logickou hodnotu podle toho, jestli je daná hodnota prvkem daného seznamu. Můžeš se inspirovat příkladem s hádankou (viz výše).

ikona boxu
Nápověda

Úlohu si dobře promysli:

  • Co přesně ja zadáním?
  • Co má být vstupem funkce, co výstupem?
  • Jak obdobu stejné úlohy řešíš ručně, na papíře?
  • Co si při provádění musíš pamatovat? Kdy a jak se rozhoduješ? Co se opakuje?
  • Jak takový postup dobře popsat?
  • Vytvoříš nejdřív slovní popis, vývojový diagram, nebo rovnou zdrojový kód v Pythonu?

Svoje řešení nezapomeň otestovat.


Řešení

def JeVSeznamu (HledanyPrvek, Seznam) :
    for Prvek in Seznam :
    if Prvek == HledanyPrvek :
        return True
    return False


Následně můžeš novou funkci použít v samotné hádance a tím její zdrojový kód zjednodušit. Zkus to!

Řešení

def Hadanka () :
    OdpovedUzivatele = input("ZADANI HADANKY")
    SpravneOdpovedi = ["Prvni moznost", "Druha moznost"]
    if JeVSeznamu (OdpovedUzivatele, SpravneOdpovědi) :
        print("Spravne, dobra prace!")
    else :
        print("Je mi lito, to neni spravne.")