====== Den 4 ====== ===== Opakování z minulých dnů ===== ===Úkol 1=== Naprogramujte výpočet hodnoty čísla Pi pomocí metody Monte Carlo (viz výklad na cvičení). * Na vstupu uživatel zadá, na kolik desetinných míst by chtěl Pi spočítat. * Po každých sto iteracích uložte současný mezivýsledek do seznamu. Seznam po doběhnutí výpočtu vytiskněte, abychom demonstrovali postupné zpřesňování výsledku. Pokud bude mít seznam více než sto prvků, vytiskněte pouze prvních sto. Pro kontrolu dosavadní přesnosti můžete váš výsledek porovnat s konstantou pi, která je již v Pythonu k dispozici. import math error = abs(moje_pi - math.pi) == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ===Úkol 2=== Vytvořte program, který vypíše zadaný měsíc zadaného roku společně s jeho jménem. Je nanejvýše vhodné zvolit nějaký pevný bod (datum včetně dne v týdnu) od kterého se bude výpočet odvíjet. Přehlednosti kalendíře lze nejsnáze dosáhnout formátovaným výstupem: print("%2d " % (number)) Při tvorbě programu nejprve zanebejte přestupné roky. Až vám bude program fungovat, přidejte **přestupné roky** -- každý rok, který je beze zbyku dělitelný čtyřmi, s výjimkou celých století, která nejsou beze zbytku dělitelná 400 __**Příklad:**__ **Vstup** Zadejte rok: 2017 Zadejte mesic: 9 **Výstup** ZARI 2017 PO UT ST CT PA SO NE 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 == Řešení - Bez ošetření přestupných roků == //Bude zveřejněno na konci čtvrtého dne.// /* # Jirka days = ("PO", "UT", "ST", "CT", "PA", "SO", "NE") months = ("Leden", "Unor", "Brezen", "Duben", "Kveten", "Cerven", "Cervenec", "Srpen", "Zari", "Rijen","Listopad","Prosinec") monthsLen = (31,28,31,30,31,30,31,31,30,31,30,31) def printCalendary(year, month, beginDay): # hlavicka jmeno mesice a rok print(months[month-1],year) # tisknuti zkratek jmen dnu for i in range(0,7): print(days[i]+" ",end="") print() # tisknuti mezer -- odsazeni 1. dne mesice for i in range(0,beginDay): print(" ",end="") # tisknuti dnu v mesici for i in range(1,monthsLen[month-1]+1): print("%2d " % (i), end="") # zalomeni u nedele if((i+beginDay)%7 == 0): print() print() print() def countBeginning(year, month): # rozdil v rocich od referencniho data dr = year-2018 # rozdil v dnech na zaklade roku dd = dr*365 # rozdil ve dnech na zaklade mesicu for i in range(0,month-1): dd += monthsLen[i] dd %= 7 return dd year = int(input("Zadejte rok: ")) month = int(input("Zadejte mesic: ")) diff = countBeginning(year,month) printCalendary(year,month,diff) */ ===Úkol 3=== Napište program, který provede skalární součin dvou vektorů. Na vstupu uživatel zadá rozměr obou vektorů. Program pak **náhodně vygeneruje** dva vektory specifikované délky a spočítá jejich skalární součin. Program oba vygenerované vektory i výsledek jejich skalárního součinu potom vytiskne. [[https://cs.wikipedia.org/wiki/Skal%C3%A1rn%C3%AD_sou%C4%8Din|Skalární součin (Wikipedia)]] je součet součinů jednotlivých prvků ve vektoru. **Příklad** [a1 a2 a3 ... an]. [b1 b2 b3 ... bn] = a1.b1 + a2.b2 + a3.b3 + ... an.bn Pro větší přehlednost vypistujte čísla naformátovaná na pevný počet desetinných míst (například 2). __**Příklad**__ **Vstup** Zadejte dimenzi (rozmer) vektoru: 2 **Výstup** [0.25 2].[3 7.5] = 15.75 __**Návrh na rozšíření**__ Program namísto vygenerování dvou náhodných vektorů umožní uživateli postupně načíst oba vektory a případně ošetří jejich různou délku. == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ===Úkol 4=== Napište program, který sečte po prvcích dvě matice. Na vstupu zadá uživatel velikost obou matic. Program poté **náhodně vygeneruje** a [[https://cs.wikipedia.org/wiki/S%C4%8D%C3%ADt%C3%A1n%C3%AD_matic|sečte matice]]. Po ukončení výpočtu program vytiskne obě vygenerované matice a výslednou matici. __**Příklad**__ **Vstup** Zadejte dimenzi (rozmer) matic: 2 **Výstup** 3 1.5 2.1 1 + 0.5 2 1 1.3 = 3.5 3.5 3.1 2.3 následně upravte program tak aby provedl: - odčítání - násobení všech prvků matice číslem Poté spojte předchozí operace a vytvořte kalkulačku, které předáte velikost matice a operaci. Pro větší přehlednost vypistujte čísla naformátovaná na pevný počet desetinných míst. __**Příklad**__ **Vstup** Zadejte dimenzi matic: 2 Zadejte operaci (+/-/*): - **Výstup** 4 3.5 1.5 12.5 - 3 1.2 1 11 = 1 2.3 0.5 1.5 **Vstup** Zadejte dimenzi matic: 2 Zadejte operaci (+/-/*): * **Výstup** 4 * 0.5 1.2 11 10.5 = 2 4.8 44 42 __**Návrh na rozšíření**__ Upravte program tak, aby jednotlivé matice mohl uživatel přímo zadat. == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ===Úkol 5=== Již známe funkci **sort()**, která seřadí prvky seznamu vzestupně podle velikosti. Napište nyní program/funkci, který pomocí algoritmu //Selection sort// (viz výklad na cvičení) daný seznam seřadí bez použití funkce **sort()**. Rychlejší z vás si mohou //Selection sort// nastudovat sami -- například [[https://cs.wikipedia.org/wiki/%C5%98azen%C3%AD_v%C3%BDb%C4%9Brem|zde]] == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ===== Funkce ===== Funkce je uzavřený, znovupoužitelný blok kódu, který umí splnit nějaký konkrétní úkol. Definovanou funkci pak můžeme kdekoliv v našem programu zavolat jedním příkazem. Po jejím zavolání se funkce celá provede a splní námi požadovaný úkol. Definice funkcí se zejména vyplatí v případech, kdy máme v našem programu nějaký dílčí úlok, který během programu potřebujeme vykonávat na více různých místech. Když řešení takového dílčího úkolu budeme mít definované jako samostatnou funkci, velmi nám to ušetří psaní a zpřehlední kód. ====Definice funkce:==== Každá funkce má **název**, kterým funkci budeme později volat. Dále musíme definovat, jestli do funkce budou vstupovat nějaké **vstupní paramtery** (vstupní proměnné), se kterými bude funkce pracovat. Vstupních proměnných může být libovolně mnoho a mohou být libovolných datových typů. Následuje samotný funkční kód. Pokud má funkce vracet nějakou návratovou hodnotu, použijeme pro její vrácení klíčové slovo **return.** V jazyce Python obecně vypadá definice funkce takto: def nazev_funkce(vstupni_parametry): kod, který ma byt vykonan * Za názvem funkce vždy musí být závorky. V případě, že funkce nepříjmá žádné vstupní parametry, necháme závorky prázdné. ====Příklady definic a volání funkcí==== Zde je příklad definice funkce //say_hello// bez vstupních parametrů a bez návratové hodnoty, která má vytisknout pozdrav. def say_hello(): print("Dobry den, vitejte u vykladu funkci.") A zde je příklad definice funkce //get_max//, která hledá větší ze dvou čísel. Vstupními parametry jsou dvě čísla k posouzení a návratovou hodnotou nalezené maximum. def get_max(a, b): if (a > b): return a else: return b Nadefinované funkce pak můžeme kdekoliv ve svém programu zavolat. Příklad programu, kde využijeme uvedené funkce může vypadat třeba takto: say_hello() number1 = 5 number2 = 3 print("Zkusime najit vetsi cislo z dvojice cisel " + str(number1) + " a " + str(number2) + ".") bigger_number = get_max(number1, number2) print("Vetsi je cislo " + str(bigger_number) + ".") Takový program bude mít následující výstup: Dobry den, vitejte u vykladu funkci. Zkusime najit vetsi cislo z dvojice cisel 5 a 3. Vetsi je cislo 5. ====Úkoly==== ===Úkol 1=== Definujte dvě metody sloužící k výpočtu a posouzení hodnoty BMI (body mass index). * První metoda přijme dva parametry: hmotnost člověka a jeho výšku. Funkce vrátí hodnotu BMI takového člověka. BMI = hmotnost[kg] / výška[m] 2 * Druhá metoda přijme jako parametr hodnotu BMI. Pokud je BMI < 18, vypíše "Podvyziveny", pro BMI v intervalu <18, 30> vypíše "V poradku" a pro BMI > 30 vypíše "Trpi nadvahou". ===Úkol 2=== Vytvořte knihovnu funkcí jednoduché pokladny. V modulu //pokladna.py// definujte následující funkce: * Funkce **read_input()**, která od uživatele postupně načítá ceny jednotlivých položek (kladná čísla). Načítání uživatel ukončí zadáním čísla 0. Funkce pak vrátí načtené částky uložené v poli. * Funkce **get_sum(array)**, která jako vstupní parametr přijme pole s čísly (načtenými částkami) a vrátí jejich součet. * Funkce **get_max(array)**, která v poli najde nejvyšší částku. * Funkce **get_min(array)**, která v poli najde nejmenší částku. * Funkce **get_avg(array)**, která spočítá průměrnou cenu na jednu položku z částek uložených v poli. * Funkce **array_contains(array, number)** která vrátí //True// nebo //False// podle toho, jestli pole obsahuje nějakou konkrétní částku (číslo). * Funkce **do_payment()**, která načte částky k zaplacení a o útratě nám vypíše informace, které umíme pomocí výše zadaných funkcí zjistit. Nyní ve stejné složce, kde máte uložený soubor pokladna.py vytvořte jiný soubor, importujte si do něj svou knihovnu pokladních funkcí a vyzkoušejte, že je můžete volat i v jiném souboru, než kde jsou definovány. Import můžeme provést dvojím způsobem.\\ Jedna možnost je importovat celý modul se všemi funkcemi v něm definovanými. Když provedeme import tímto způsobem a budeme z něj chtít zavolat nějakou funkci, provedeme to obecně příkazem ve tvaru //nazev_modulu.nazev_funkce(parametry)//. V našem programu to může vypadat takto: import pokladna bill = pokladna.read_input() ... Druhá možnost je importovat pouze nějakou konkrétní funkci, ne celý modul. V tomto případě při volání funkce neuvádíme název modulu, v němž je funkce definována. Tento postup by vypadal následovně: from pokladna import read_input bill = read_input() ... Při tvorbě větších programů je běžné vytvářet si pro určité typy úkonů knihovny funkcí a pak si tyto funkce jinde v projektu pouze volat. Díky tomuto konceptu se mohou projekty dělit do menších souborů a udržovat přehledné. Zároveň nám většina programovacích jazyků nabízí velké množství vlastních knihoven s funkcemi, jejichž využíváním si mnohdy můžeme výrazně ušetřit práci (v Pythonu jsme se zatím setkali s knihovnou //math// a //random//). == Řešení == # Simon def read_input(): pole=[] a=float(input("Zadej cenu produktu: ")) while a!=0: castka.append(a) a=float(input("Zadej cenu produktu: ")) return(pole) def get_sum(pole): soucet=0 for i in range(0, len(pole)): soucet=soucet+pole[i] return soucet def get_max(pole): pole.sort() return pole[len(pole)-1] def get_min(pole): pole.sort() return pole[0] def get_avg(pole): soucet=0 for i in range(0, len(pole)): soucet=soucet+pole[i] return soucet/len(pole) def array_contains(pole, cislo): for i in range(0, len(pole)): if pole[i]-cislo==0: final=pole[i] return True break if i==len(pole)-1: return False break def do_payment(pole): print("Utrata dela "+str(get_sum(pole))+"Kc.") print("Nejlevnejsi polozka je za "+str(get_min(pole))+"Kc.") print("Nejdrazsi polozka je za "+str(get_max(pole))+"Kc.") print("Prumerna cena polozky je "+str(get_avg(pole))+"Kc.") === Úkol 3 === Vytvořte funkci, které je předán jako parametr seznam čísel a dva číselné indexy. Funkce v seznamu prohodí čísla na zadaných pozicích. cisla = [5, 7, 6, 8] prohod(cisla, 1, 2) print(cisla) # vypise [5, 6, 7, 8] === Úkol 4 === Vytvořte funkci, které je předán jako parametr seznam čísel. Funkce v seznamu nalezne nejmensi cislo a vrati jeho index (pozici). cisla = [7, 6, 5, 8] i = nejmensi(cisla) x = cisla[i] print(x) # vypise cislo 5 === Úkol 5 === Použijte funkci nejmensi() z předchozího úkolu a vytvorte funkci serad(), které je předán jako parametr seznam čísel. Funkce čísla v seznamu seřadí od nejmenšího po největší. /* ===== Práce ve vývojovém prostředí PyCharm ===== Doposud jsme všechny psali programy v textovém editoru Gedit a následně jsme je pouštěli z terminálového okna. Tento proces lze zjednodušit a zrychlit použitím vývojového prostředí PyCharm. To také nabízí řadu dalších užitečných nástrojů. Prostředí spustíte zadáním následujícího příkazu do terminálu. charm Poté se vám zobrazí úvodní okno. Po kliknutí na možnost "Create New Project" se zobrazí okno dotazující se na adresář, ve kterém chcete projekt vytvořit. Vyberte adresář "Programovani". V levé liště se vám zobrazí všechny programy uložené ve složce "Programovani". Dvojklikem na vybraný soubor ho otevřete k editaci. Nový program vytvoříte výběrem "File" > "New..." v horním menu a následným výběrem možnosti "Python File". Po zadání jména se program vytvoří. Pokud chcete program spustit, klikněte pravým tlačítkem myši na soubor v levé liště a vyberte "Run". Poté můžete tento program spouštět kliknutím na zelený trojúhelník v pravé části horní lišty. Po spuštění programu se ve spodní části obrazovky zobrazí konzolové okno, které je obdobou terminálového okna operačního systému. V tomto okně se tedy zobrazují i výpisy funkce print, případně do něj uživatel zadává vstupní hodnoty pro funkce input a raw_input. ===== Debugování a ladění programu ===== Při psaní složitějších programů je téměř nemožné vyhnout se chybám. Ty mohou způsobit, že program nebude fungovat správně (spočítá nesprávný výsledek), případně může dojít k situaci, kdy program "spadne". Hledání takových chyb pouhým pozorováním napsaného kódu může být (nejen časově) velmi vyčerpávající. K efektivnímu hledání chyb nabízí PyCharm možnost debugování, která umožňuje pozastavení programu, zobrazení aktuálních hodnot proměnných a následné spuštění zbytku programu. ==== Příklad 1 ==== Vytvořte program "prohod_cisla" a vložte do něj následující kód. a = 5 b = 10 a = b b = a print("a = " + str(a)) print("b = " + str(b)) Již podle názvu je zřejmé, že se od programu očekává, že prohodí hodnoty v proměnných "a" a "b". Po spuštění ale program vypíše chybný výsledek. Chybu jste jistě hned nalezli, nicméně se na chvíli tvařme, jako že nevíme, v čem je problém. Nejprve musíme do kódu vložit tzv. breakpointy, kterými označíme místa, kde chceme program pozastavit. Breakpoint do kódu vložíte kliknutím vedle čísla řádku. Stejným způsobem můžete breakpoint odstranit. Řádky s vloženými breakpointy jsou zvýrazněny červeně. Vložte do vašeho kódu breakpoint na první řádek, viz následující obrázek. {{ :courses:pri-bootcamp:obr_breakpoint.png |}} Nyní spusťe debugování kliknutím na ikonku zeleného brouka v pravé části horní lišty. {{ :courses:pri-bootcamp:obr_debug.png |}} Program se spustí a pozastaví hned na prvním řádku, resp. program se pozastaví před vykonáním příkazu na tomto řádku. Výběrem "Run" > "Step Over" v horním menu (případně stiskem kláves F8) se tento příkaz vykoná a program se pozastaví na dalším řádku. Takto můžeme krok za krokem projít celým kódem (proto se tomuto způsobu hledání chyb říká také krokování). V dolní části obrazovky je na místě konzolového okna zobrazeno okno "Variables", ve kterém jsou zobrazeny všechny používané proměnné s jejich aktuální hodnotou. Pokud takto dojdeme ke čtvrtému řádku, zjistíme, že po jeho vykonání je v obou proměnných "a" i "b" hodnota 10. Původní hodnotu proměnné "a" jsme tedy ztratili, protože příkaz na řádku čtyři ji nahradil hodnotou "b". Nyní když jsme chybu nalezli, můžeme vykonat zbytek programu volbou "Run" > "Resume Program" (případně stiskem klávesy F9). Pokud by program takto narazil na další breakpoint, opět by se pozastavil. ==== Příklad 2 ==== Vytvořte nyní nový program s názvem "fce_faktorial" a vložte do něj následující kód. def faktorial(n): f = 1 for i in range(1, n): f = f*i return f n = input("Zadejte prirozene cislo nebo nulu: ") x = faktorial(n) print("Faktorial zadaneho cisla je") print(x) Po spuštění programu zjistíme, že nevypočítá správnou hodnotu faktoriálu a je v něm tedy chyba. Vložte breakpoint na řádek, kde se načítá hodnota do proměnné "n". Při debugování je nutné k vykonání tohoto příkazu přepnout spodní okno na konzoli, zadat vstupní hodnotu a následně přepnout okno zpět na debuger. Při použití možnosti "Step Over" na dalším řádku zjistíte, že program vykoná celou funkci faktorial najednou jako jeden příkaz. To je očekávané a správné chování, nicméně by v tuto chvíli bylo užitečné krokovat i funkci faktorial. Toho docílíme vložením breakpointu do těla (dovnitř) funkce. Další možností je použití volby "Run" > "Step Into" (případně stiskem klávesy F7), která při debugování vstoupí do uživatelem definovaných funkcí. Nálezt chybu ve výše uvedém programu pro vás jistě nebude problém. ==== Shrnutí ==== Při hledání chyb nejprve vložte na požadovaná místa v programu breakpointy. Po spuštění debugování použijte následující možnosti * Step Into (F7) * Step Over (F8) * Resume Program (F9) Samozřejmě ne vždycky je nutné uchýlit se k debugování. V případě kratších programů je často jednoduší a rychlejší zkontrolovat kód bez debugování. */ ===== Volitelné úkoly ===== ===Rekurze=== //“To iterate is human, to recurse divine.”// (L. Peter Deutsch) Nejjednodušší případ rekurzivní funkce, je taková funkce, která musí k vypočítání výsledku **zavolat sebe sama**. Například: def odecitej(x): return odecitej(x-1) Pokud vám stále ještě není jasné co je to rekurze, přečtěte si tuto větu ještě jednou... ;-) Funkce **odecitej** má ale jednu malou vadu. Nikdy nevrátí výsledek. Proto je potřeba mít v každé rekurzivní funkci takzvaný "**base case**", který ukončí rekurzivní volání. def odecitej_do_nuly(x): if x == 0: # base case return 0 # konec rekurze return odecitej_do_nuly(x-1) Funkce **odecitej_do_nuly** má stále ještě jeden problém. Odhalíte jaký? ===Úkoly=== Při řešení následujících úkolů nesmíte použít klíčová slova __for__ ani __while__! ==Úkol 1== Vytvořte pole obsahujicí deset náhodných čísel pomocí rekurze. == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ==Úkol 2== Naimplementujte funkci **faktorial(x)**, která vypočítá **x!** pomocí rekurze. == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ==Úkol 3== Napiště funkci **fibonacci(x)**, která vrátí x-tý prvek fibonacciho posloupnosti. == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ==Úkol 4== Vytvořte matici (2D pole) 5x5. matice = [ # matice 2x2 [1,2], [3,4], ] matice[1][1] # vrati 4 matice[0][1] # vrati 2 Vyplňte tuto matici čísly od 24 do 0 funkcí **jméno_funkce(matice)**, a poté vypište matici na standartní výstup. [[24, 23, 22, 21, 20], [19, 18, 17, 16, 15], [14, 13, 12, 11, 10], [9, 8, 7, 6, 5], [4, 3, 2, 1, 0]] == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ==Úkol 5 (Náročnější!)== Máte šachové pole 9x9 a figurku jezdce na pozici (4,4). Označte všechny políčka šachovnice, na které jezdec může doskákat do tří tahů, hodnotou 1 a ostatní políčka hodnotou nula. Vypiště šachovnici na standartní výstup. Jezdec nemusí využít všechny své tahy. K vypsání můžete použít for cyklus. # takto ma vypadat vystup 001010100 010101010 100111001 011101110 101010101 011101110 100111001 010101010 001010100 # prvni tah # druhy tah # treti tah | | | | | X X X | | | | | | X X X X | | | | X X | |X XXX X| | | | X X | | XXX XXX | | X | -> | X | -> |X X X X X| | | | X X | | XXX XXX | | | | X X | |X XXX X| | | | | | X X X X | | | | | | X X X | == Řešení == //Bude zveřejněno na konci čtvrtého dne.// /* */ ==Úkol 6 (oddechove)== Choose you favourite [[http://www.devtopics.com/101-great-computer-programming-quotes/|Computer Quote]] 8-)