![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Jak jsme si mohli všimnout, je přípustné provést více než jedno přiřazení k téže proměnné. Nové přiřazení způsobí, že ke stávající proměnné je přiřazena nová hodnota, čímž proměnná přestane odkazovat ke staré hodnotě.
bruce = 5 print bruce, bruce = 7 print bruce
Výstup z tohoto skriptu je 5 7, protože ve chvíli, kdy je bruce poprvé tištěn, má hodnotu 5, při druhém výstupu má hodnotu 7. Čárka na konci prvního příkazu print potlačí přechod na nový řádek, což je příčinou toho, že se oba výstupy objeví na stejném řádku.
Ilustrace měněného přiřazení:

Při měněném přiřazení je zejména důležité rozlišovat mezi operací přiřazení a příkazem rovnosti. Protože Python používá znaménko (=) pro přiřazení, je lákavé interpretovat přikaz a = b jako příkaz rovnosti. Tak tomu není!
Za prvé, rovnost je vztah komutativní, přiřazení nikoliv. Na příklad, v matematice když napíšeme a = 7, pak také platí, že 7 = a. V Pythonu je příkaz a = 7 přípustný a 7 = a nikoli.
Navíc, v matematice je přikázání rovnosti vždy pravdivé. Je-li a = b nyní, bude tomu tak pořád. Příkaz přiřazení v Pythonu může vytvořit dvě stejné proměnné, ty se však mohou změnit:
a = 5 b = a # a i b jsou nyní stejné a = 3 # a i b již nejsou stejné
Třetí řádek mění hodnotu a, ale nemění hodnotu b, takže již nejsou stejné. (Aby nedocházelo k záměnám, je v některých jazycích symbol pro přiřazení odlišný: <- nebo a:=.)
Jednou z nejobvyklejších forem měněného přiřazení je aktualizace (update) či obnova, kde nová hodnota proměnné závisí na staré hodnotě.
x = x + 1Zápis říká: "Ke stávající hodnotě x přičti 1 a touto novou hodnotou obnov původní proměnnou x".
Při pokusu o obnovu neexistující proměnné obdržíme chybu, protože Python vyhodnotí výraz na pravé straně operátoru přiřazení předtím než výslednou hodnotu přiřadí ke jménu na levé straně:
>>> x = x + 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'x' is not defined
Proměnnou musíme nejprve zavést (initialize), abychom ji mohli obnovit; stačí prosté přiřazení:
>>> x = 0 >>> x = x + 1 >>>
Aktualizace proměnné přičtením 1 se nazývá jednotkový přírůstek (increment); odečtením 1 - jednotkový úbytek (decrement).
Počítače se často používají k automatickému výpočtu opakujících se úloh. Opakované provádění stejných nebo podobných úloh bez chyb je něco, co počítače dělají lépe než lidé.
Opakované provádění řady příkazů se nazývá iterace. Protože iterace je často používaná, poskytuje Python ve svém jazyce několik prvků k jejímu usnadnění. Nejprve se seznámíme s příkazem while.
Použití příkazu while si ukážeme na funkci countdown:
def countdown(n): while n > 0: print n n = n-1 print "Odpal!"
Příkaz while se dá číst téměř jako prostý text: "Pokud bude n > 0, zobrazuj hodnotu n a potom ji zmenši o 1. Když se dostaneš k 0, zobraz slovo Odpal!"
Formálněji popíšeme tok výpočtu s příkazem while takto:
Tělo tvoří všechny příkazy pod záhlavím se stejným odsazením.
Tento typ toku programu se nazývá smyčka, protože se ve třetím kroku 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. Nekonečný zdroj zábavy pro programátory jsou poznámky na adresu některých reklamních výzev, jako je "Namydli, spláchni, opakuj", což je neuzavřený cyklus, neboli nekonečná smyčka.
V případě funkce countdown 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, if n%2 == 0: # n is even n = n/2 else: # n is odd n = n*3+1
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. Například, pro sequence(3) je výsledná posloupnost 3, 10, 5, 16, 8, 4, 2 .
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 hodnoty n. Zatím se to nikomu nepodařilo!
K efektivnímu psaní počítačových programů potřebuje programátor vyvinout schopnost zaznamenat průběh běžícího programu. Znamená to "stát se počítačem" a sledovat průběh provádění, zaznamenávajíc stavy všech proměnných a všechny výstupy, které program generuje po provedení jednotlivých instrukcí.
Abychom porozuměli tomuto procesu, sledujme volání fce sequence(3) z předchozího odstavce. Na počátku záznamu máme lokální proměnnou n (parametr fce) s počáteční hodnotou 3. Protože 3 není totožné 1, provede se smyčka while. 3 je tištěno a 3%2 == 0 je hodnoceno. Protože je vyhodnoceno jako False, provede se větev else, kde se hodnota výrazu 3*3+1 přiřadí k n.
Při ručním zápisu si pro každou proměnnou a výstup vytvoříme záhlaví sloupce; v našem příkladě asi takto:
n output --- ------ 3 3 10
Protože 10 != 1 se vyhodnotí jako True, provádí se opět tělo smyčky a tiskne se 10. Jelikož podmínka 10%2 == 0 je pravdivá, provádí se větev if a n získává hodnotu 5. Na konci záznamu máme:
n output --- ------ 3 3 10 10 5 5 16 16 8 8 4 4 2 2 1Provádění záznamu může být poněkud únavné a náchylné k chybám (však to také většinou děláme pomocí počítače!) ale je nezbytnou dovedností programátora. Z tohoto záznamu se dozvíme mnohé o tom, jak náš kód pracuje. Můžeme si povšimnout, že jakmile se n se stane mocninou 2, potřebuje tělo smyčky log2(n) cyklů ke svému ukončení. Také vidíme, že konečná hodnota 1 se nevytiskne.
Následující funkce spočítá počet číslic kladného celého čísla v desetinném formátu:
def num_digits(n): count = 0 while n: count = count+1 n = n/10 return count
Volání num_digits(710) vrátí 3. Průběh funkce si ověřme zápisem jejího volání.
Na této funkci si můžeme ukázat další prvek výpočtu zvaný počítadlo. Proměnná count je zavedena s hodnotou 0 a potom zvětšována o 1 při každém provedení smyčky. Když smyčka skončí, obsahuje count výsledek - celkový počet provedení (cyklů) smyčky, což je také počet číslic zadaného čísla.
Kdybychom chtěli počítat jenom číslice s hodnotou 0 nebo 5, pomůžeme si přidáním podmínky před zvyšováním stavu počítadla:
def num_zero_and_five_digits(n): count = 0 while n: digit = n%10 if digit == 0 or digit == 5: count = coount+1 n = n/10 return countPřesvědč se, že num_zero_and_five_digits(1055030250) vrací 7.
Zvětšování proměnné je tak běžné, že pro něj Python poskytuje zkrácenou syntaxi:
>>> count = 0 >>> count += 1 >>> count 1 >>> count += 1 >>> count 2 >>>count += 1 je zkráceným zápisem count = count + 1. Přírůstkovou hodnotou nemusí být jednička:
>>> n = 2 >>> n+= 5 >>> n 7 >>>Jsou také zkratky pro -=, *=, /=, a %= :
>>> n = 2 >>> n*= 5 >>> n 10 >>> n-= 4 >>> n 6 >>> n/= 2 >>> n 3 >>> n%= 5 >>> n 1
Jedním z vhodných použití smyčky je generování tabelárních dat. Předtím, než byly počítače běžně přístupné, musely být logaritmy, siny, kosiny a jiné funkce čísel počítány ručně. Pro ulehčení výpočtu obsahovaly matematické příručky dlouhé tabulky s uspořádaným hodnotami těchto funkcí. Vytváření tabulek bylo zdlouhavé, nudné a občas plné chyb.
Když se na scéně objevily počítače, jedna z prvních reakcí byla: "Sláva! Můžeme tvořit tabulky na počítači, který nedělá chyby." To se ukázalo být (většinou) pravdivé, ale i krátkozraké. Brzy nato se staly kalkulátory a počítače tak všudepřítomné, že se tabulky staly zbytečné.
Tedy téměř. Pro některé operace používají počítače tabulky hodnot jako přibližnou mezihodnotu, kterou potom v dalším výpočtu upřesňují. Někdy byly chybné právě tyto tabulky; nejproslulejší jsou ty, které Intel Pentium používal k výpočtu dělení v plovoucí řádové čárce.
Byť tabulka logaritmů již není tak užitečná jako bývala, poslouží nám jako dobrý příklad iterace (opakovaného výpočtu). Následující program vytvoří posloupnost hodnot v levém sloupci a příslušné mocniny čísla 2 v pravém:
x = 1 while x < 13: print x, '\t', 2**x x +=1
Řetězec '\t' zastupuje znak tab. Zpětné lomítko označuje začátek "escape" sekvence, což je pořadí několika určených znaků, které nahrazují neviditelné znaky např. pro tab a nový řádek. Sekvence \n zastupuje nový řádek.
"Escape" sekvence se může použít kdekoliv v řetězci; v našem příkladě je v řetězci pouze znak pro tab. Jak zapíšeme pro řetězec zpětné lomítko?
Jak jsou postupně znaky a řetězce zobrazovány na obrazovce, jsou kontrolovány neviditelným hlídačem, zvaným kurzor, který zaznamenává jejich polohu. Po příkazu print přechází kurzor obvykle na počátek dalšího řádku.
Znak tab posune kurzor doprava k nejbližší zarážce. Taby jsou vhodné pro tvorbu uspořádaných řádků jako má výstup z předchozího programu:
1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024 11 2048 12 4096
Protože mezi sloupci máme znaky tab, není pozice druhého sloupce závislá na počtu číslic v prvním sloupci.
Dvojrozměrná tabulka je tabulka ve které odečítáme údaje v průsečíku řádku a sloupce. Dobrým příkladem je tabulka násobení. Řekněmež, že chceme zobrazit tabulku pro násobení čísel od 1 do 6.
Dobrým začátkem je zápis smyčky, která vytiskne všechny násobky dvou na jeden řádek:
i = 1 while i <= 6: print 2*i, ' ', x += 1 print
První řádek vytvoří proměnnou i, která působí jako počítadlo, neboli proměnnou smyčky. Při procházení smyčkou se hodnota i zvětšuje od 1 do 6. Při každém průchodu se zobrazí hodnota 2*i, následovaná třemi mezerami.
Čárka v příkazu print opět potlačí nový řádek. Po ukončení jednoho cyklu smyčky začíná příkaz print nový řádek.
Výstup programu je tento:
2 4 6 8 10 12
Výborně, jen tak dál. Dalším krokem bude zapouzdření a generalizace.
Zapouzdření je proces zabalení části kódu do funkce, umožňující využít všechny výhody, kterými funkce disponují. Viděli jsme již dva příklady zapouzdření: print_parity v kapitole 4 a is_divisible v kapitole 5.
Zobecněním se míní přetvoření specifické funkce, jako je například tvorba násobků dvou, tak aby tiskla násobky jakéhokoliv čísla.
Následující funkce zapouzdří předchozí smyčku a zobecní ji tak, aby vracela násobky n.
def print_multiples(n): i = 1 while i <= 6: print n*i, '\t', i += 1 print
Všechno, co jsme pro zapouzdření museli udělat bylo to, že jsme přidali první řádek, který deklaruje jméno funkce a seznam parametrů. Pro zobecnění nám stačilo vyměnit hodnotu 2 parametrem n
Zavoláme-li tuto funkci pro argument 2, dostaneme stejný výstup jako předtím. Pro argument 3 obdržíme:
3 6 9 12 15 18
Pro argument 4 je výstup:
4 8 12 16 20 24
Nyní patrně už umíme odhadnout jak vytvořit tabulku pro násobení - opakovaným voláním print_multiples pro různé argumenty. Vlastně můžeme použít ještě jednu smyčku:
i = 1 while i <= 6: print_multiples(i) i += 1
Všimněme si, jak podobná je tato smyčka té, která je uvnitř print_multiples. Výměna příkazu print voláním funkce print_multiples bylo vše, co jsme učinili.
Výstup tohoto programu je tabulka pro násobení:
1 2 3 4 5 6 2 4 6 8 10 12 3 6 9 12 15 18 4 8 12 16 20 24 5 10 15 20 25 30 6 12 18 24 30 36
Abychom si ještě jednou předvedli zapouzdření, vezměme kód z předcházejícího odstavce a zabalme jej do funkce:
def print_mult_table(): i = 1 while i <= 6: print_multiples(i) i += 1
Tento proces je běžná rozvojová metoda. Vytvářený kód zapisujeme do řádek mimo funkci, případně jej zkoušíme v překladači. Jakmile nám chodí, zabalíme jej do funkce.
Tato metoda je zvláště užitečná, když na počátku ještě nevíme, jak rozdělit program do funcí. Program tak vzniká postupným vývojem.
Možná je nám divné, že můžeme použít tutéž proměnnou i jak v print_multiples, tak v print_mult_table. Nezpůsobuje to problémy, když jedna z funkcí mění hodnotu proměnné?
Odpověď zní ne, protože i v print_multiples a i v print_mult_table nejsou tytéž proměnné.
Proměnné, vytvořené uvnitř funkce jsou lokální; mimo svou domovskou funkci nejsou přístupné. Můžeme tedy mít více proměnných se stejným jménem, pokud se neobjeví ve stejné funkci.
Zobrazení zásobníku pro tento program ukazuje, že proměnné i vskutku nejsou tytéž. Mohou se vztahovat k různým hodnotám a změna jedné neovlivní druhou.

Hodnota i v print_mult_table se mění od 1 do 6. Ve schematu to je 3. V příštím cyklu smyčky to bude 4. Při každém cyklu volá print_mult_table funkci print_multiples s aktuální hodnotou i jako argumentem. Tato hodnota se přiřadí parametru n.
Uvnitř funkce print_multiples se hodnota i mění také od 1 do 6. Ve schematu to je 2. Změna této hodnoty nemá žádný vliv na hodnotu i v print_mult_table
Je obvyklé a zcela legální používat různé lokální proměnné se stejným jménem. Zvláště jména i a j se často používají jako proměnné smyčky. Vyhýbat se jim v jedné funkci, protože jsme je použili v jiné, může vést ke zhoršené čitelnosti programu.
Pro další příklad zobecnění předpokládejme, že chceme program, který by vytiskl tabulku násobení jakékoliv velikosti, nikoliv pouze šest krát šest. Můžeme přidat parametr do print_mult_table:
def print_mult_table(high): i = 1 while i <= high: print_multiples(i) i = i + 1
Hodnotu 6 jsme nahradili parametrem high. Zavoláme-li print_mult_table(7), zobrazí se:
1 2 3 4 5 6 2 4 6 8 10 12 3 6 9 12 15 18 4 8 12 16 20 24 5 10 15 20 25 30 6 12 18 24 30 36 7 14 21 28 35 42
To je prima, až na to, že asi chceme, aby byla tabulka čtvercová - se stejným počtem řádků jako sloupců. Zajistíme to přidáním dalšího parametru do print_multiples abychom určili, kolik sloupců by tabulka měla mít.
Tento parametr schválně nazveme high, abychom předvedli, že různé funkce mohou mít parametr se stejným jménem (stejně jako lokální proměnné). Zde je celý program:
def print_multiples(n,high): i = 1 while i <= high: print n*i, '\t', i += 1 print def print_mult_table(high): i = 1 while i <= high: print_multiples(i, high) i += 1
Povšimněme si, že s přidáním nového parametru jsme museli změnit první řádek funkce (záhlaví funkce), a také jsme museli změnit místo, kde je funkce v print_mult_table volána.
Dle očekávání generuje program čtvercovou tabulku 7x7:
1 2 3 4 5 6 7 2 4 6 8 10 12 14 3 6 9 12 15 18 21 4 8 12 16 20 24 28 5 10 15 20 25 30 35 6 12 18 24 30 36 42 7 14 21 28 35 42 49
Provedeme-li zobecnění vhodným způsobem, často dostaneme program se schopnostmi, které jsme nezamýšleli. Například si lze všimnout, že v důsledku toho, že ab = ba, všechny hodnoty se v tabulce opakují dvakrát. Mohli bychom ušetřit inkoust kdybychom tiskli jen polovinu tabulky. Zařídíme to pouhou změnou jednoho řádku v print_mult_table. Změňme
print_multiples(i, high)
na
print_multiples(i, i)
a obdržíme
1 2 4 3 6 9 4 8 12 16 5 10 15 20 25 6 12 18 24 30 36 7 14 21 28 35 42 49
Několikrát již byly zmiňovány výhodné vlastnosti funkcí. Pojďme si teď říci, jaké výhody to jsou:
Smyčky jsou časo 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 vpodstatě 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 approxVolejme funkci pro argument 25, abychom se přesvědčili, že výsledek je 5.0
Newtonova metoda je příkladem algoritmu - mechanického způsobu řešení jisté kategorie problémů (v našem případě počítání druhé odmocniny).
Definovat algoritmus není snadné. Když jsme se učili násobit jednomístná čísla, tak jsme se vlastně naučili 100 konkrétních řešení.
Pokud jsme však byli "líní", mohli jsme se naučit několika trikům. Například, pro součin čísla n a 9 zapíšeme n-1 jako první číslici a 10-n jako druhou číslici výsledku. Tento trik je obecným řešením pro násobení libovolného jednomístného čísla číslem 9. Toto je algoritmus!
Podobnými algoritmy jsou techniky, které jsme se naučili pro sčítání s převáděním, odčítání s půjčováním a dělení. Jedním z rysů algoritmu je, že ke svému provedení nevyžaduje nadměrnou inteligenci. Jsou to mechanické procesy, kde jeden krok vyplývá z předchozího podlé jednoduchého souboru pravidel.
Dle našeho názoru je zarážející, že lidé stráví tolik času ve škole pro zvládnutí algoritmů, které vlastně nevyžadují žádnou inteligenci.
Proces navrhování algoritmu je na druhé straně zajímavý, intelektuálně náročný a je středobodem toho, čemu říkáme programování.
Některé věci, které lidé dělají zcela přirozeně, bez potíží a nevědomky, se nejobtížněji vyjadřují algoritmicky. Dobrým příkladem je chápání přirozeného jazyka. Všichni jej používáme, ale dosud nebyl nikdo schopen vysvětlit jak to děláme, přinejmenším ne v algoritmické formě.
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
stvori tento vystup
1 1 2 3 3 6 4 10 5 15(Nápověda: zjisti si na webu jak se takový součet počítá, pokud to náhodou nevíš. )
if __name__ == '__main__': import: doctest doctest.testmod()Napiš funkci is_prime, která vyhodnotí zadané celé číslo a vrátí True pro argument, který je prvočíslem a False pro argument, který provočíslem není. Při vývoji funkce používej "doctesty".
Co vrátí fce num_digits(0)? Uprav ji tak, aby pro tento případ vrátila 1. Proč volání num_digits(-24) skončí nekonečnou smyčkou? (Nápověda: -1/10 se vyhodnotí jako -1. ) Uprav num_digits tak, aby pracovala správně pro jakoukoli celočíselnou hodnotu.
Následující kód přidej do souboru ch06.py, který jsi vytvořil v předešlé úloze:
def num_digits(n): """ >>> num_digits(12345) 5 >>> num_digits(0) 1 >>> num_digits(-12345) 5 """Přidej své tělo funkce a přesvědč se, že projde sítem doctestů.
def num_even_digits(n): """ >>> num_even_digits(123456) 3 >>> num_even_digits(2468) 4 >>> num_even_digits(1357) 0 >>> num_even_digits(2) 1 >>> num_even_digits(20) 2 """Přidej své tělo funkce a přesvědč se, že projde sítem doctestů.
def print_digits(n): """ >>> print_digits(13789) 9 8 7 3 1 >>> print_digits(39874613) 3 1 6 4 7 8 9 3 >>> print_digits(213141) 1 4 1 3 1 2 """Přidej své tělo funkce a přesvědč se, že projde sítem doctestů.
def sum_of_squares_of_digits(n): """ >>> sum_of_squares_of_digits(1) 1 >>> sum_of_squares_of_digits(9) 81 >>> sum_of_squares_of_digits(11) 2 >>> sum_of_squares_of_digits(121) 6 >>> sum_of_squares_of_digits(987) 194 """Své řešení ověř pomocí doctestů.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |