5. Podprogramy¶
Funkcie¶
Doteraz sme pracovali so štandardnými funkciami, napríklad
vstup a výstup
input()
aprint()
aritmetické funkcie
abs()
around()
generovanie postupnosti čísel pre for-cyklus
range()
Všetky tieto funkcie niečo vykonali (vypísali, prečítali, vypočítali, …) a niektoré z nich vrátili nejakú hodnotu, ktorú sme mohli ďalej spracovať. Tiež sme videli, že niektoré majú rôzny počet parametrov, prípadne sú niekedy volané bez parametrov.
Okrem toho sme pracovali aj s funkciami, ktoré boli definované v iných moduloch:
keď napíšeme
import random
, môžeme pracovať, napríklad s funkciamirandom.randint()
arandom.randrange()
keď napíšeme
import math
, môžeme pracovať, napríklad s funkciamimath.sin()
amath.cos()
keď napíšeme
import tkinter
, môžeme pracovať, napríklad s funkciamitkinter.Canvas()
atkinter.mainloop()
Všetky tieto a tisícky ďalších v Pythone naprogramovali programátori pred nejakým časom, aby nám neskôr zjednodušili samotné programovanie. Vytváranie vlastných funkcií pritom vôbec nie je komplikované a teraz sa to naučíme aj my.
Funkcie
Funkcia je pomenovaný blok príkazov (niekedy sa tomu hovorí aj podprogram). Popisujeme (definujeme) ju špeciálnou konštrukciou:
def meno_funkcie(): # zapamätaj si blok príkazov ako nový príkaz
prikaz
prikaz
...
Keď zapíšeme definíciu funkcie, zatiaľ sa z bloku príkazov (hovoríme tomu telo funkcie) nič nevykoná. Táto definícia sa „len“ zapamätá a jej referencia sa priradí k zadanému menu - vlastne sa do premennej meno_funkcie
priradí referencia na telo funkcie. Je to podobné tomu, ako sa priraďovacím príkazom do premennej priradí hodnota z pravej strany príkazu.
Ako prvý príklad zapíšme takúto definíciu funkcie:
def vypis():
print('**********')
print('**********')
Zadefinovali sme funkciu s menom vypis
, pričom telo funkcie obsahuje dva príkazy na výpis riadkov s hviezdičkami. Celý blok príkazov je odsunutý o 4 medzery rovnako ako sme odsúvali príkazy v cykloch a aj v podmienených príkazoch. Definícia tela funkcie končí vtedy, keď sa objaví riadok, ktorý už nie je odsunutý. Touto definíciou sa ešte žiadne príkazy z tela funkcie nevykonávajú. Na to potrebujeme túto funkciu zavolať.
Volanie funkcie
Volanie funkcie je taký zápis, ktorým sa začnú vykonávať príkazy z definície funkcie. Stačí zapísať meno funkcie so zátvorkami a funkcie sa spustí:
meno_funkcie()
Samozrejme, že funkciu môžeme zavolať až vtedy, keď už Python pozná jej definíciu.
Zavolajme funkciu vypis
v príkazovom režime:
>>> vypis()
**********
**********
>>>
Vidíme, že sa vykonali oba príkazy z tela funkcie a potom Python ďalej čaká na ďalšie príkazy. Zapíšme volanie funkcie aj s jej definíciou priamo do skriptu (teda v programovom režime):
def vypis():
print('**********')
print('**********')
print('hello')
vypis()
print('* Python *')
vypis()
Skôr, ako to spustíme, si uvedomme, čo sa udeje pri spustení:
zapamätá sa definícia funkcie v premennej
vypis
vypíše sa slovo
'hello'
zavolá sa funkcia
vypis()
vypíše riadok s textom
'* Python *'
znovu sa zavolá funkcia
vypis()
A teraz to spustíme:
hello
**********
**********
* Python *
**********
**********
Zapíšme teraz presné kroky, ktoré sa vykonajú pri volaní funkcie:
preruší sa vykonávanie práve bežiaceho programu (Python si presne zapamätá miesto, kde sa to stalo)
skočí sa na začiatok volanej funkcie
postupne sa vykonajú všetky príkazy
keď sa príde na koniec funkcie, zrealizuje sa návrat na zapamätané miesto, kde sa prerušilo vykonávanie programu a pokračuje sa vo vykonávaní ďalších príkazov za volaním funkcie
Pre volanie funkcie sú veľmi dôležité okrúhle zátvorky. Bez nich to už nie je volanie, ale len zisťovanie referencie na hodnotu, ktorá je priradená pre toto meno. Napríklad:
>>> vypis()
**********
**********
>>> vypis
<function vypis at 0x0205CB28>
Ak by sme namiesto volania funkcie takto zapísali len meno funkcie bez zátvoriek, ale v skripte (teda nie v interaktívnom režime), táto hodnota referencie by sa nevypísala, ale odignorovala. Toto býva dosť častá chyba začiatočníkov, ktorá sa ale ťažšie odhaľuje.
Ak zavoláme funkciu, ktorú sme ešte nedefinovali, Python vyhlási chybu, napríklad:
>>> vipis()
...
NameError: name 'vipis' is not defined
Samozrejme, že môžeme volať len definované funkcie.
>>> vypis()
**********
**********
>>> vypis = 'ahoj' # tu sme zmenili obsah premennej vypis
>>> vypis
'ahoj'
>>> vypis()
...
TypeError: 'str' object is not callable
Hodnotou premennej vypis
je už teraz znakový reťazec, a ten sa „nedá zavolať“, t.j. nie je „callable“ (tento objekt nie je zavolateľný ako funkcia).
Funkcie kreslia do grafickej plochy¶
Napíšme teraz funkciu, ktorá do grafickej plochy na náhodnú pozíciu napíše nejaký text, napríklad 'PYTHON'
:
def kresli_text():
x = randint(50, 330)
y = randint(20, 240)
canvas.create_text(x, y, text='PYTHON')
Aby sme túto funkciu mohli zavolať, musí už exitovať randint
aj canvas
, teda
import tkinter
from random import randint
def kresli_text():
x = randint(50, 330)
y = randint(20, 240)
canvas.create_text(x, y, text='PYTHON')
canvas = tkinter.Canvas()
canvas.pack()
for i in range(20):
kresli_text()
V tejto malej ukážke vidíme tieto novinky:
v tele funkcie môžeme používať premenné, ktoré sú definované mimo tela funkcie (
randint
acanvas
) - neskôr uvidíme, že im budeme hovoriť globálne premennév tele funkcie sme do dvoch premenných
x
ay
priradili nejaké hodnoty a ďalej sme ich používali v ďalšom príkaze funkcie - neskôr uvidíme, že im budeme hovoriť lokálne premenné

Parametre funkcií¶
Hotové funkcie, s ktorými sme doteraz pracovali, napríklad print()
alebo random.randint()
, mali aj parametre, vďaka čomu riešili rôzne úlohy. Parametre slúžia na to, aby sme mohli funkcii lepšie oznámiť, čo špecifické má urobiť: čo sa má vypísať, z akého intervalu má vygenerovať náhodné číslo, akú úsečku má nakresliť, prípadne akej farby, …
Parametre funkcie
Parametrom funkcie je dočasná premenná, ktorá vzniká pri volaní funkcie a prostredníctvom ktorej, môžeme do funkcie poslať nejakú hodnotu. Parametre funkcií definujeme počas definovania funkcie v hlavičke funkcie a ak ich je viac, oddeľujeme ich čiarkami:
def meno_funkcie(parameter):
prikaz
prikaz
...
Môžeme napríklad zapísať:
def vypis_hviezdiciek(pocet):
print('*' * pocet)
V prvom riadku definície funkcie (hlavička funkcie) pribudla jedna premenná pocet
- parameter. Táto premenná vznikne automaticky pri volaní funkcie, preto musíme pri volaní oznámiť hodnotu tohto parametra. Volanie zapíšeme:
>>> vypis_hviezdiciek(30)
******************************
>>> for i in range(1, 10):
vypis_hviezdiciek(i)
*
**
***
****
*****
******
*******
********
*********
Pri volaní sa „skutočná hodnota“ priradí do parametra funkcie (premenná pocet
).
Už predtým sme popísali mechanizmus volania funkcie, ale to sme ešte nepoznali parametre. Teraz doplníme tento postup o spracovanie parametrov. Najprv trochu terminológie:
pri definovaní funkcie v hlavičke funkcie uvádzame tzv. formálne parametre: sú to nové premenné, ktoré vzniknú až pri volaní funkcie
pri volaní funkcie musíme do zátvoriek zapísať hodnoty, ktoré sa stanú tzv. skutočnými parametrami: tieto hodnoty sa pri volaní priradia do formálnych parametrov
Mechanizmus volania vysvetlíme na volaní vypis_hviezdiciek(30)
:
zapamätá sa návratová adresa volania
vytvorí sa nová premenná
pocet
(formálny parameter) a priradí sa do nej hodnota skutočného parametra30
vykonajú sa všetky príkazy v definícii funkcie (telo funkcie)
zrušia sa všetky premenné, ktoré vznikli počas behu funkcie
riadenie sa vráti na miesto, kde bolo volanie funkcie
Zapíšme novú funkciu cerveny_kruh()
, ktorá bude mať dva parametre: súradnice stredu kruhu. Funkcia nakreslí kruh s polomerom 20 s daným stredom:
def cerveny_kruh(x, y):
canvas.create_oval(x-10, y-10, x+10, y+10, fill='red')
a môžeme ju zavolať napríklad takto:
import tkinter
def cerveny_kruh(x, y):
canvas.create_oval(x-10, y-10, x+10, y+10, fill='red')
canvas = tkinter.Canvas()
canvas.pack()
for x in range(30, 350, 25):
for y in range(20, 240, 15):
cerveny_kruh(x, y)

Aj v tomto príklade si popíšme Mechanizmus volania funkcie:
zapamätá sa návratová adresa volania
vytvoria sa dve nové premenné
x
ay
(formálne parametre) a priradia sa do nej hodnoty skutočných parametrov30
a20
(prvé volanie funkcie)vykonajú sa všetky príkazy v definícii funkcie (telo funkcie)
zrušia sa všetky premenné, ktoré vznikli počas behu funkcie, teda
x
ajy
riadenie sa vráti na miesto, kde bolo volanie funkcie
Už vieme, že priraďovací príkaz vytvára premennú a referenciou ju spojí s nejakou hodnotou. Premenné, ktoré vzniknú počas behu funkcie, sa stanú lokálnymi premennými: budú existovať len počas tohto behu a po skončení funkcie, sa automaticky zrušia. Aj parametre vznikajú pri štarte funkcie a zanikajú pri jej skončení: tieto premenné sú pre funkciu tiež lokálnymi premennými.
V nasledovnom príklade funkcie vypis_sucet()
počítame a vypisujeme súčet čísel od 1 po zadané n
:
def vypis_sucet(n):
sucet = 1
print(1, end=' ')
for i in range(2, n + 1):
sucet = sucet + i
print('+', i, end=' ')
print('=', sucet)
Pri volaní funkcie sa pre parameter n
= 10 vypíše:
>>> vypis_sucet(10)
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = 55
Počas behu vzniknú 2 lokálne premenné (sucet
a i
) a jeden parameter, ktorý je pre funkciu tiež lokálnou premennou:
n
vznikne pri štarte funkcie aj s hodnotou 10sucet
vznikne pri prvom priradenísucet = 1
i
vznikne pri štarte for-cyklu
Po skončení behu funkcie sa všetky tieto premenné automaticky zrušia.
Pozrime sa na lokálne premenné, ktoré vznikajú vo funkcii nahodne_kruhy(n, farba)
:
def nahodne_kruhy(n, farba):
for i in range(n):
x = randint(50, 330)
y = randint(20, 240)
canvas.create_oval(x-10, y-10, x+10, y+10, fill=farba)
Napríklad:
import tkinter
from random import randint
canvas = tkinter.Canvas()
canvas.pack()
nahodne_kruhy(100, 'red')
nahodne_kruhy(50, 'blue')
nahodne_kruhy(10, 'yellow')
Program postupne nakreslí na náhodné pozície 100 červených, 50 modrých a 10 žltých kruhov.

Všimnite si vo funkcii nahodne_kruhy()
tri lokálne premenné (i
, x
, y
) a dva parametre (n
, farba
), ktoré sú pre funkciu tiež lokálne premenné.
Menný priestor¶
Aby sme lepšie pochopili ako naozaj fungujú lokálne premenné, musíme rozumieť, čo to je a ako funguje menný priestor (namespace). Najprv trochu ďalšej terminológie: všetky identifikátory v Pythone sú jedným z troch typov (Python má pre identifikátory 3 rôzne tabuľky mien):
štandardné, napríklad
int
,print
, …hovorí sa tomu builtins
globálne - definujeme ich na najvyššej úrovni mimo funkcií, napríklad funkcie
vypis_hviezdiciek
,vypis_sucet
,nahodne_kruhy
, alebo aj premennérandrint
,tkinter
,canvas
(zrejmetkinter
je referenciou na importovaný modul)hovorí sa tomu main
lokálne - vznikajú počas behu funkcie
Tabuľka štandardných mien (builtins) je pre celý program len jedna, tiež tabuľka globálnych mien (main) je len jedna, ale každá funkcia má svoju „súkromnú“ lokálnu tabuľku mien, ktorá vznikne pri štarte (zavolaní) funkcie a zruší sa pri konci vykonávania funkcie.
Keď na nejakom mieste použijeme identifikátor, Python ho najprv hľadá (v tzv. menných priestoroch):
v lokálnej tabuľke mien, ak tam tento identifikátor nenájde, hľadá ho
v globálnej tabuľke mien, ak tam tento identifikátor nenájde, hľadá ho
v štandardnej tabuľke mien
Ak nenájde v žiadnej z týchto tabuliek, hlási chybu NameError: name 'identifikátor' is not defined
.
Príkaz (štandardná funkcia) dir()
vypíše tabuľku globálnych mien. Hoci pri štarte Pythonu by táto tabuľka mala byť prázdna, obsahuje niekoľko špeciálnych mien, ktoré začínajú aj končia znakmi '__'
:
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
Keď teraz vytvoríme nejaké nové globálne mená, objavia sa aj v tejto globálnej tabuľke:
>>> premenna = 2015
>>> def funkcia():
pass
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__',
'funkcia', 'premenna']
Podobne sa vieme dostať aj k tabuľke štandardných mien (builtins):
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', ...
Takto sa vypíšu všetky preddefinované mená. Vidíme medzi nimi, napríklad 'int'
, 'print'
, 'range'
, 'str'
, …
S týmito tabuľkami súvisí aj príkaz na zrušenie premennej.
príkaz del
Príkazom del
zrušíme identifikátor z tabuľky mien. Formát príkazu:
del premenná
Príkaz najprv zistí, v ktorej tabuľke sa identifikátor nachádza (najprv pozrie do lokálnej a keď tam nenájde, tak do globálnej tabuľky) a potom ho z tejto tabuľky vyhodí. Príkaz ale nefunguje pre štandardné mená.
Ukážme to na príklade: identifikátor print
je menom štandardnej funkcie (v štandardnej tabuľke mien). Ak v priamom režime (čo je globálna úroveň mien) do premennej print
priradíme nejakú hodnotu, toto meno vznikne v globálnej tabuľke:
>>> print('ahoj')
ahoj
>>> print=('ahoj') # do print sme priradili nejakú hodnotu
>>> print
'ahoj'
>>> print('ahoj')
...
TypeError: 'str' object is not callable
Teraz už print
nefunguje ako funkcia na výpis hodnôt, ale len ako obyčajná globálna premenná. Ale v štandardnej tabuľke mien print
stále existuje, len je táto premenná prekrytá globálnym menom. Python predsa najprv prehľadáva globálnu tabuľku a až keď sa tam nenájde, hľadá sa v štandardnej tabuľke. A ako môžeme vrátiť funkčnosť štandardnej funkcie print
? Stačí vymazať identifikátor z globálnej tabuľky:
>>> del print
>>> print('ahoj')
ahoj
Vymazaním globálneho mena print
ostane definovaný len identifikátor v tabuľke štandardných mien, teda opäť začne fungovať funkcia na výpis hodnôt.
Pozrime sa teraz na prípad, keď sa v tele funkcie bude nachádzať volanie inej funkcie (tzv. vnorené volanie), napríklad:
def vypis_hviezdiciek(pocet):
print('*' * pocet)
def trojuholnik(n):
for i in range(1, n+1):
vypis_hviezdiciek(i)
Pri ich definovaní v globálnom mennom priestore vznikli dva identifikátory: vypis_hviezdiciek
a trojuholnik
. Zavoláme funkciu trojuholnik
:
>>> trojuholnik(5)
Najprv sa pre túto funkciu vytvorí jej menný priestor (lokálna tabuľka mien) s dvomi lokálnymi premennými: n
a i
. Teraz pri každom (vnorenom) volaní vypis_hviezdiciek(i)
sa pre túto funkciu:
vytvorí nový menný priestor s jedinou premennou
pocet
vykoná sa príkaz
print()
nakoniec sa zruší jej menný priestor, t.j. zanikne premenná
pocet
Môžeme to odkrokovať pomocou http://www.pythontutor.com/visualize.html#mode=edit (zapneme voľbu Python 3.6
):
najprv do editovacieho okna zapíšeme nejaký program, napríklad:
spustíme vizualizáciu pomocou tlačidla Visualize Execution a potom niekoľkokrát tlačíme tlačidlo Forward >
Všimnite si, že v pravej časti tejto stránky sa postupne zobrazujú menné priestory (tu sa nazývajú frame):
najprv len globálny priestor s premennými
vypis_hviezdiciek
atrojuholnik
potom sa postupne objavujú a aj miznú lokálne priestory týchto dvoch funkcií - na obrázku vidíme oba tieto menné priestory tesne pred ukončením vykonávania funkcie
trojuholnik
s parametrom2
Grafické funkcie¶
Zadefinujeme funkcie, pomocou ktorých sa nakreslí 5000 náhodných farebných bodiek, ktoré budú zafarbené podľa nejakých pravidiel:
import tkinter
from random import randint
from math import sqrt
def vzd(x1, y1, x2, y2):
return sqrt((x1-x2) ** 2 + (y1-y2) ** 2)
def kresli_bodku(x, y, farba):
canvas.create_oval(x-5, y-5, x+5, y+5, fill=farba, width=0)
def farebne_bodky(pocet):
for i in range(pocet):
x = randint(10, 290)
y = randint(10, 290)
if vzd(x, y, 150, 150) > 100:
kresli_bodku(x, y, 'navy')
elif vzd(x, y, 230, 150) > 100:
kresli_bodku(x, y, 'red')
else:
kresli_bodku(x, y, 'yellow')
canvas = tkinter.Canvas(width=300, height=300)
canvas.pack()
farebne_bodky(5000)
Funkcia vzd()
počíta vzdialenosť dvoch bodov (x1, y1)
a (x2, y2)
v rovine - tu sa použil známy vzorec z matematiky. Táto funkcia nič nevypisuje, ale vracia číselnú hodnotu (desatinné číslo). Ďalšia funkcia kresli_bodku()
nič nevracia, ale vykreslí v grafickej ploche malý kruh s polomerom 5, ktorý je zafarbený zadanou farbou. Tretia funkcia farebne_bodky()
dostáva ako parameter počet bodiek, ktoré má nakresliť: funkcia na náhodné pozície nakreslí príslušný počet bodiek, pričom tie, ktoré sú od bodu (150, 150)
vzdialené viac ako 100, budú tmavomodré (farba 'navy'
), tie, ktoré sú od bodu (230, 150)
vzdialené viac ako 100, budú červené a všetku ostatné budú žlté. Všimnite si, že sme samotný program zapísali až za definíciami všetkých funkcií. Po spustení dostávame približne takýto obrázok:

Náhradná hodnota parametra¶
Naučíme sa zadefinovať parametre funkcie tak, aby sme pri volaní nemuseli uviesť všetky hodnoty skutočných parametrov, ale niektoré sa automaticky dosadia, tzv. náhradnou hodnotou (default), napríklad:
def kresli_bodku(x, y, farba='red', r=5):
canvas.create_oval(x-r, y-r, x+r, y+r, fill=farba, width=0)
V hlavičke funkcie môžeme k niektorým parametrom uviesť náhradnú hodnotu (vyzerá to ako priradenie). V tomto prípade to označuje, že ak tomuto formálnemu parametru nebude zodpovedať skutočný parameter, dosadí sa práve táto náhradná hodnota. Pritom musí platiť, že keď nejakému parametru v definícii funkcie určíte, že má náhradnú hodnotu, tak náhradnú hodnotu musíte zadať aj všetkým ďalším formálnym parametrom, ktoré sa nachádzajú v zozname parametrov za ním (ak sme zadefinovali náhradnú hodnotu pre parameter farba
, musíme nejakú zadefinovať aj pre parameter r
).
Teraz môžeme zapísať aj takéto volania tejto funkcie:
kresli_bodku(100, 200, 'blue', 3) # farba bude 'blue' a r bude 3
kresli_bodku(150, 250, 'blue') # farba bude 'blue' a r bude 5
kresli_bodku(200, 200) # farba bude 'red' a r bude 5
Parametre volané menom¶
Funkcia kresli_bodku
má štyri parametre: x
, y
, farba
a r
.
Python umožňuje funkcie s parametrami volať tak, že skutočné parametre neurčujeme pozične (prvému skutočnému zodpovedá prvý formálny, druhému druhý, atď.) ale priamo pri volaní uvedieme meno parametra. Takto môžeme určiť hodnotu ľubovoľného parametra. Napríklad všetky tieto volania sú korektné:
kresli_bodku(10, 20, r=10)
kresli_bodku(farba='green', x=10, y=20)
kresli_bodku(r=7, farba='yellow', y=20, x=30)
Samozrejme aj pri takomto volaní môžeme vynechať len tie parametre, ktoré majú určenú náhradnú hodnotu, všetky ostatné parametre sa musia v nejakom poradí objaviť v zozname skutočných parametrov.
Farebný model RGB¶
Keďže už vieme vytvárať reťazce so šestnástkovým zápisom čísel (napríklad 'f{číslo:02x}'
alebo '{:02x}'.format(číslo)
) zapíšeme funkciu rgb()
, ktorá bude vytvárať farby pomocou RGB-modelu:
def rgb(r, g, b):
return f'#{r:02x}{g:02x}{b:02x}'
otestujme:
>>> rgb(255, 255, 0)
'#ffff00'
>>> rgb(0, 100, 0)
'#006400'
Funkciu rgb()
môžeme využiť, napríklad na kreslenie farebných štvorcov:
import tkinter
canvas = tkinter.Canvas()
canvas.pack()
def rgb(r, g, b):
return f'#{r:02x}{g:02x}{b:02x}'
def stvorec(strana, x, y, farba=''):
canvas.create_rectangle(x, y, x + strana, y + strana, fill=farba)
for i in range(10):
stvorec(30, i*30, 10, rgb(100+16*i, 0, 0))
stvorec(30, i*30, 50, rgb(100+16*i, 0, 255-26*i))
stvorec(30, i*30, 90, rgb(26*i, 26*i, 26*i))
stvorec(30, i*30, 130, rgb(0, 26*i, 26*i))
Tento program nakreslí takýchto 40 zafarbených štvorcov:

Náhodné farby¶
Ak potrebujeme generovať náhodnú farbu, ale stačí nám iba jedna z dvoch možností, môžeme to urobiť, napríklad takto:
def nahodna2_farba():
if random.randrange(2):
return 'blue'
return 'red'
Už ste sa možno stretli s tým, že by mohlo fungovať aj random.choice(('blue', 'red'))
.
Podobne by sa zapísala funkcia, ktorá generuje náhodnú farbu jednu z troch a pod.
Ak ale chceme úplne náhodnú farbu z celej množiny všetkých farieb, využijeme RGB-model, napríklad takto:
def rgb(r, g, b):
return f'#{r:02x}{g:02x}{b:02x}'
def nahodna_farba():
return rgb(random.randrange(256), random.randrange(256), random.randrange(256))
Už vieme, že sa to dá zapísať aj takto:
def nahodna_farba():
return f'#{random.randrange(256**3):06x}'
Môžeme vygenerovať štvorcovú sieť náhodných farieb:
import tkinter
from random import randrange
def nahodna_farba():
return f'#{random.randrange(256**3):06x}'
canvas = tkinter.Canvas()
canvas.pack()
for y in range(0, 230, 30):
for x in range(0, 350, 30):
stvorec(26, x, y, nahodna_farba())
Dostaneme nejaký takýto obrázok:

Niekoľko užitočných matematických funkcií¶
Na záver ukážeme sériu zaujímavých matematických funkcií. Mnohé z nich sme už programovali predtým, ale teraz ich uvidíme v tvare funkcií. Začneme s vypis_delitele(cislo)
, ktorá do jedného riadka vypíše všetky delitele daného čísla:
def vypis_delitele(cislo):
for i in range(1, n+1):
if cislo % i == 0:
print(i, end=' ')
print()
>>> vypis_delitele(24)
1 2 3 4 6 8 12 24
Ďalšia funkcia sucet_delitelov(cislo)
tieto delitele nevypisuje, ale vráti ich súčet (pomocou return
):
sucet_delitelov(cislo):
vysl = 0
for i in range(1, n+1):
if cislo % i == 0:
vysl += i
return vysl
>>> sucet_delitelov(24)
60
>>> sucet_delitelov(11)
12
Funkcia je_dokonale(cislo)
pomocou funkcie sucet_delitelov()
zistí, či je dané číslo dokonalé, t.j. že súčet všetkých menších deliteľov ako samotné číslo sa rovná samotnému číslu. Napríklad delitele čísla 6
(menšie ako 6
) sú 1
, 2
, 3
. Ich súčet je 6
. Preto je číslo 6
dokonalé. Funkcia nič nevypisuje, ale vracia (pomocou return
) True
alebo False
.
def je_dokonale(cislo)
return sucet_delitelov(cislo) == 2*cislo
>>> je_dokonale(6)
True
>>> je_dokonale(24)
False
Ďalšia funkcia vsetky_dokonale(od, do)
vypíše všetky dokonalé čísla v danom intervale <od, do>
. Táto funkcia využije funkciu je_dokonale()
:
def vsetky_dokonale(od, do):
for cislo in range(od, do+1):
if je_dokonale(cislo):
print(cislo, 'je dokonalé')
>>> vsetky_dokonale(1, 30)
6 je dokonalé
28 je dokonalé
Zapíšeme funkciu nsd(a, b)
, ktorá počíta najväčší spoločný deliteľ dvoch čísel. Použijeme tzv. Euklidov algoritmus, ktorý už pred vyše 2 tisíc rokmi popísal starogrécky matematik Euklides:
def nsd(a, b):
while a != b:
if a > b:
a = a - b
else:
b = b - a
return a
>>> nsd(60, 18)
6
>>> nsd(100000000, 1)
1
Iste ste si všimli, že volanie nsd(100000000, 1)
trvá niekoľko sekúnd. Zrejme preto, lebo while-cyklus 100000000
-krát odpočíta 1
. Pritom si stačí uvedomiť, že takýto cyklus:
while a > b:
a = a - b
pre kladné čísla a
a b
urobí to isté ako:
a = a % b
teda zvyšok po delení. Funkciu nsd
môžeme teraz výrazne vylepšiť:
def nsd(a, b):
while b != 0:
a, b = b, a % b
return a
Odkrokujte tento algoritmus pomocou stránky http://www.pythontutor.com/visualize.html#mode=edit.
Ďalšia funkcia pocet_delitelov(cislo)
pre dané číslo zistí počet všetkých deliteľov. Napríklad delitele čísla 6
sú 1
, 2
, 3
, 6
, preto funkcia vráti 4
. Funkcia nič nevypisuje, ale vracia (pomocou return
) celé číslo:
def pocet_delitelov(cislo):
vysl = 0
for i in range(1, n+1):
if cislo % i == 0:
vysl += 1
return vysl
>>> pocet_delitelov(6)
4
>>> pocet_delitelov(17)
2
Teraz funkcia je_prvocislo(cislo)
pomocou funkcie pocet_delitelov()
veľmi jednoducho zistí (vráti True
alebo False
), či je to prvočíslo (je deliteľné len 1
a samým sebou):
def je_prvocislo(cislo):
return pocet_delitelov(cislo) == 2
>>> je_prvocislo(6)
False
>>> je_prvocislo(17)
True
Ďalšia funkcia vsetky_prvocisla(od, do)
vypíše do jedného riadka všetky prvočísla v danom intervale:
def vsetky_prvocisla(od, do):
for cislo in range(od, do+1):
if je_prvocislo(cislo):
print(cislo, end=' ')
print()
>>> vsetky_prvocisla(1, 30)
2 3 5 7 11 13 17 19 23 29
Aj tento algoritmus odkrokujte pomocou stránky http://www.pythontutor.com/visualize.html#mode=edit.
Programátori veľmi obľubujú štandardné funkcie min
a max
, ktoré vrátia minimálny, resp. maximálnu hodnotu z dvoch daných hodnôt. Zapíšme tieto dve funkcie s použitím príkazu if
:
def min(a, b):
if a < b:
return a
return b
def max(a, b):
if a > b:
return a
return b
Vďaka tejto definícii funkcií, nemôže používať štandardné funkcie min
a max
. Hovoríme, že sme ich prekryli novými definíciami. Ak by sme teraz chceli vytvoriť funkciu min
, ktorá ale zistí minimum troch čísel, nebude fungovať:
def min(a, b, c):
return min(min(a, b), c)
lebo sme opäť prekryli našu pôvodnú funkciu min
novšou verziou. Mohli by sme to zapísať napríklad takro:
def min2(a, b):
if a < b:
return a
return b
def min(a, b, c):
return min2(min2(a, b), c)
Už v druhej prednáške sme počítali faktoriál pomocou for-cyklu. Zapíšme to do funkcie:
def faktorial(n):
vysl = 1
for i in range(2, n+1):
vysl *= i
return vysl
Z tréningových dôvodov to môžeme zapísať aj pomocou while-cyklu:
def faktorial(n):
vysl = 1
while n > 1:
vysl *= n
n -= 1
return vysl
Opäť vidíte, že sme tu použili parameter n
ako lokálnu premennú. Funkciu faktorial
by sme mohli využiť na výpočet kombinačného čísla:
def kombinacne_cislo(n, k):
return faktorial(n) // (faktorial(n-k) * faktorial(k))
Hoci je toto správny zápis, matematici vedia, že sa to dá počítať aj výrazne efektívnejšie. Veď, napríklad pre kombinacne_cislo(100, 2)
musí táto funkcia počítať dva veľké faktoriály 100!
a 98!
a potom ich medzi sebou deliť, pritom by stačilo vypočítať 100*99//2
. Kombinačné číslo sa dá vypočítať ako súčin k
čísel od n
smerom dole vydelené k
faktoriálom. Zapíšme:
def kombinacne_cislo(n, k):
vysl = 1
for i in range(n+1-k, n+1):
vysl *= i
return vysl // faktorial(k)
Ďalším veľmi známym matematickým pojmom je Fibonacciho postupnosť. Táto postupnosť celých čísel začína dvomi číslami 0
a 1
a každé ďalšie sa počíta ako súčet dvoch predchádzajúcich členov postupnosti. Veľmi rýchlo by sme vedeli zapísať niekoľko prvých členov postupnosti:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
Napíšme funkciu, ktorá vypočíta n
-ty člen tejto postupnosti (prvky budeme počítať od nultého, ktorého hodnota je 0
):
def fibonacci(n):
if n < 2:
return n
f1, f2 = 0, 1
for i in range(n-1):
f1, f2 = f2, f1+f2
return f2
Niekedy môžete vidieť aj takýto zápis:
def fibonacci(n):
a, b = 0, 1
while n:
a, b = b, a+b
n -= 1
return a
Cvičenia¶
L.I.S.T.
riešenia odovzdávaj na úlohový server https://list.fmph.uniba.sk/
používaj len konštrukcie z doterajších prednášok
pozri si Riešenie úloh 5. cvičenia
Napíš funkciu
obdlznik(sirka, znak='*')
, ktorá z daného znakuznak
vypíše do troch riadkov výstupu obdĺžnik zadanej šírky. Napríklad pre volania:obdlznik(30, '#') obdlznik(6) obdlznik(19, 'O')
dostaneme výstup:
############################## # # ############################## ****** * * ****** OOOOOOOOOOOOOOOOOOO O O OOOOOOOOOOOOOOOOOOO
Napíš funkciu
riadok(n, text='')
, ktorá vypíšen
znakový reťazec hviezdičiek'*'
, stred ktorého nahradí zadaným textom. Ak je tento zadanýtext
neprázdny, vloží na jeho začiatok aj koniec medzeru. Napríklad pre volania:sir = 40 riadok(sir) riadok(sir, 'Ján Botto') riadok(sir, 'Žltá ľalija') riadok(sir, '-') riadok(sir, 'Stojí stojí mohyla') riadok(sir, 'Na mohyle zlá chvíľa') riadok(sir, 'na mohyle tŕnie chrastie') riadok(sir, 'a v tom tŕní chrastí rastie') riadok(sir)
dostaneme výstup:
**************************************** ************** Ján Botto *************** ************* Žltá ľalija ************** ****************** - ******************* ********** Stojí stojí mohyla ********** ********* Na mohyle zlá chvíľa ********* ******* na mohyle tŕnie chrastie ******* ***** a v tom tŕní chrastí rastie ****** ****************************************
Na prednáške si sa zoznámil s funkciou
nsd(a, b)
, ktorá počítala najväčší spoločný deliteľ dvoch čísel. Inšpiruj sa touto funkciou a napíš funkciunsn(a, b)
, ktorá vypočíta najmenší spoločný násobok dvoch čísel. Napríklad pre volania:a, b = 129, 162 print(f'nsn({a}, {b}) =', nsn(a, b)) a, b = 60, 168 print(f'nsn({a}, {b}) =', nsn(a, b))
dostaneme výstup:
nsn(129, 162) = 6966 nsn(60, 168) = 840
Na prednáške si sa zoznámil s funkciou
fibonacci(n)
, ktorá počítalan
-tý člen fibonacciho postupnosti. Napíš funkciufib_medzi(od, do)
, ktorá vypíše (pomocouprint
) všetky fibonacciho čísla z daného intervalu<od, do>
. Táto funkcia by mala obsahovať len jeden while-cyklus (okrem priradení aif
). Napríklad pre volania:fib_medzi(3, 100) fib_medzi(1000, 3000)
dostaneme výstup:
3 5 8 13 21 34 55 89 1597 2584
Na prednáške si sa zoznámil s funkciou
je_prvocislo(cislo)
, ktorá pomocou funkciepocet_delitelov(cislo)
zisťovala, či je danécislo
prvočíslo. Oprav túto funkciuje_prvocislo(cislo)
tak, aby nevyužívalapocet_delitelov(cislo)
, ale vo while-cykle zisťovala, či neexistuje aspoň jeden deliteľ v intervale<2, odmocnina>
. Funkcia sa bude postupne snažiť nájsť takého deliteľa daného čísla, ktorého druhá mocnina nie je väčšia ako dané číslo. Napríklad číslo25
bude postupne deliť2
,3
,4
,5
(pre všetky ich druhá mocnina nie je väčšia ako25
) a na5
skončí, lebo delí25
. Číslo37
sa tiež pokúsi deliť2
,3
,4
,5
,6
(žiadne z nich nie je deliteľom) a keďže pre všetky väčšie je ich druhá mocnina väčšia ako37
, vyhlásime37
za prvočíslo. Okrem funkcieje_prvocislo(cislo)
napíš aj funkciudvojicky(od, do)
, ktorá v danom intervale<od, do>
nájde všetky prvočíselné dvojičky (ich rozdiel je 2). Napríklad:>>> dvojicky(3, 50) 3 5 5 7 11 13 17 19 29 31 41 43 >>> dvojicky(1000000, 1000300) 1000037 1000039 1000211 1000213 1000289 1000291
Napíš funkciu
hadanie(od, do)
, pomocou ktorej sa budeš vedieť zahrať s počítačom takúto hru: počítač si náhodne pomyslí číslo z intervalu<od, do>
(neprezradí nám ho) a my sa ho budeme na maximálne 10 pokusov snažiť uhádnuť. Po každom pokuse nám oznámi, či náš typ je menší ako jeho číslo alebo väčší. Priebeh hry by mohol vyzerať napríklad takto:>>> hadanie(1, 100) Myslím si číslo, uhádni ho! tvoj tip: 50 *** pridaj tvoj tip: 75 *** pridaj tvoj tip: 88 *** uber tvoj tip: 81 *** uber tvoj tip: 78 *** uber tvoj tip: 77 Uhadol si na 6. pokus. Gratulujem. >>> hadanie(1, 100) Myslím si číslo, uhádni ho! tvoj tip: 10 *** pridaj tvoj tip: 20 *** pridaj tvoj tip: 30 *** pridaj tvoj tip: 40 *** pridaj tvoj tip: 50 *** pridaj tvoj tip: 60 *** uber tvoj tip: 59 *** uber tvoj tip: 58 *** uber tvoj tip: 57 *** uber tvoj tip: 56 *** uber Neuhádol si ani na 10 pokusov. Myslel som si číslo 54.
Ak sa budeš hrať so svojim programom, mal by si vždy uhádnuť aj pre interval
hadanie(1, 500)
Do daného programu dopíš dve chýbajúce funkcie
koleso
adoska
tak, aby si dostal daný obrázok.import tkinter def koleso(...): ... def doska(...): ... def vozik(x, y): doska(x, y) koleso(x-30, y) koleso(x+30, y) def velky_vozik(x, y): doska(x, y, 150, 40, 'green') koleso(x-35, y, 25, 'orange') koleso(x+35, y, 25, 'orange') canvas = tkinter.Canvas() canvas.pack() vozik(200, 100) velky_vozik(150, 200) vozik(300, 210)
Napíš funkciu
kruhy(x, y)
, ktorá nakreslí 10 sústredných náhodne zafarbených kruhov, ich polomery budú5
,10
,15
, … Napríklad pre volanie:for i in range(10): kruhy(randint(50, 330), randint(50, 210))
by sa nakreslilo:
Napíš funkciu
dom(x, y, vel1, vel2)
, ktorá nakreslí domček: štvorec má ľavý dolný roh(x, y)
a veľkosť strany jevel1
, trojuholník má výškuvel2
a základňuvel1
. Oba sú zafarbené rôznymi náhodnými farbami. Napríklad pre volanie:x, y = 10, 150 while x < 330: v = randint(20, 50) dom(x, y, v, randint(v//2, v)) x += v
by sa nakreslilo:
Na prednáške sa pomocou farebných bodiek kreslil červený mesiac na modrom pozadí. Využívala sa pritom funkcia
vzd
. Napíš funkciufarebne_bodky(r, x1, y1, x2, y2, x3, y3)
, ktorá na podobnom princípe grafickú plochu vybodkuje podľa týchto pravidiel: ak by sme nakreslili tri kruhy s polomeromr
ale s rôznymi stredmi(x1, y1)
,(x2, y2)
,(x3, y3)
, tieto by sa mohli čiastočne prekrývať. Bodky budeš farbiť tak, že tie oblasti, v ktorých nie je žiaden kruh alebo sa prekrývajú práve 2 kruhy zafarbíš na modro, ostatné oblasti budú žlté. Napríklad pre volanie:farebne_bodky(80, 120, 120, 180, 110, 160, 170)
by sa nakreslilo:
Napíš funkciu
stv(riadok, stlpec, farba='white')
, ktorá nakreslí farebný štvorec do myslenej štvorcovej siete, v ktorej je každé políčko veľké 30x30. Ľavý horný roh najľavejšieho horného štvorca má súradnice(5, 5)
. Napríklad pre volanie:for i in range(8): for j in range(12): if i == j: stv(i, j) else: stv(i, j, nahodna_farba())
by sa nakreslilo:
Napíš funkciu
rgb
(z prednášky) a pomocou nej zafarbi štvorce takto (ľavý horný štvorec má farburgb(255, 0, 0)
a pravý dolnýrgb(255, 255, 0)
, farba v ostatných štvorcoch plynulo prechádza - čím je štvorec bližšie k pravému dolnému rohu, tým je bližšie k žltej):
Napíš funkciu
n_uholnik(n, x0, y0, r)
, ktorá nakreslí pravidelnýn
-uholník. Tento n-uholník bude vpísaný v myslenej kružnici so stredom(x0, y0)
a s polomeromr
. Na výpočet bodov na kružnici použi známy vzorec:x = x0 + r * cos(radians(uhol)) y = y0 + r * sin(radians(uhol))
Napríklad pre volanie:
n_uholnik(3, 50, 50, 45) n_uholnik(4, 150, 50, 45) n_uholnik(5, 250, 50, 45) n_uholnik(6, 50, 150, 45) n_uholnik(7, 150, 150, 45) n_uholnik(8, 250, 150, 45)
by sa nakreslilo:
Napíš funkciu
n_hviezda(n, x0, y0, r, k=2)
, ktorá pracuje na rovnakom princípe akon_uholnik(n, x0, y0, r)
z predchádzajúcej úlohy. V tomto prípade sa ale nespájajú úsečkami najbližšie vrcholyn
-uholníka, ale parameterk
určuje, o koľko vrcholov sa presunieme pre každú úsečku. Napríkladn_hviezda(5, 50, 50, 45, 2)
označuje, že sa budú spájať vrcholy5
-uholníka takto: 0. vrchol s 2., potom 2. vrchol s 4., potom 4. s 1., 1. vrchol so 3. a na koniec (piata úsečka) 3. vrchol s 0. Napríklad pre volanie:n_hviezda(5, 50, 50, 45) n_hviezda(7, 150, 50, 45) n_hviezda(7, 250, 50, 45, 3) n_hviezda(9, 50, 150, 45) n_hviezda(9, 150, 150, 45, 4) n_hviezda(11, 250, 150, 45, 4)
by sa nakreslilo:
Aj nasledovná funkcia
n_spirala(n, x0, y0, r)
vychádza z riešenia funkcien_uholnik(n, x0, y0, r)
. V tomto prípade sa nebude kresliťn
-uholník, ale špirála. Každá úsečka tu bude spájať dva vrcholy lenže na stále sa zväčšujúcej kružnici so stredom(x0, y0)
. Začína sa na kružnici s polomerom5
. Prvý vrchol sa spojí s nasledujúcim ale na kružnici s polomerom o2
väčším. Takto sa pokračuje, až kým by nebol polomer väčší akor
. Napríklad pre volanie:n_spirala(5, 190, 130, 125)
dostaneme:
A pre volania:
n_spirala(3, 50, 50, 45) n_spirala(4, 150, 50, 45) n_spirala(5, 250, 50, 45) n_spirala(6, 50, 150, 45) n_spirala(7, 150, 150, 45) n_spirala(8, 250, 150, 45)
dostaneme:
Napíš dve funkcie
horna(x0, y0, r)
adolna(x0, y0, r)
, ktoré nakreslia len polovicu kružnice so stredom(x0, y0)
a s polomeromr
. Funkciahorna
by mala nakresliť len hornú polovicu adolna
len dolnú. Ak by si kreslil kružnicu ako 36-uholník, potom polovica označuje len 18 úsečiek. Napríklad volania:horna(30, 100, 30) dolna(90, 100, 30) horna(150, 100, 30) dolna(210, 100, 30) horna(270, 100, 30) dolna(330, 100, 30) for i in range(6): horna(30+60*i, 200, 30)
by nakreslili:
Napíš funkcie
rucicka(uhol, dlzka, hrubka, farba)
a hodinky(hod, min, sek), pomocou ktorých nakreslíme ručičkové hodinky. Funkciarucicka
nakreslí len jednu ručičku ako úsečku z bodu(190, 130)
pod daným uhlom, danej farby a hrúbky. Funkciahodinky
nakreslí ciferník (stačí kruh s polomerom 100) a tri ručičky pre hodiny (dĺžka60
, hrúbka10
, farba'gray'
), pre minúty (dĺžka70
, hrúbka6
, farba'black'
), pre sekundy (dĺžka80
, hrúbka2
, farba'red'
). Napríklad volanie:hodinky(8, 55, 10)
by nakreslilo:
Ak by si hodinky
zavolal
takto:from time import localtime while True: canvas.delete('all') h, m, s = localtime()[3:6] hodinky(h, m, s) canvas.update() canvas.after(1000)
ukazovali by aktuálny čas.
0. Domáce zadanie¶
L.I.S.T.
riešenie odovzdávaj na úlohový server https://list.fmph.uniba.sk/
Napíš pythonovský skript, ktorý bude definovať tieto dve funkcie:
ciferny_sucet(cislo, k=10)
- vráti ciferný súčet daného čísla vk
číselnej sústavepocet_cifier(cislo, k=10)
- vráti počet cifier daného čísla vk
číselnej sústave
Program by nemal nič vypisovať.
Tvoj odovzdaný program s menom riesenie.py
musí začínať tromi riadkami komentárov (zmeň meno a dátum odovzdania):
# 0. zadanie: cifry
# autor: Janko Hraško
# datum: 10.10.2019
V programe používaj len konštrukcie jazyka Python, ktoré sme sa učili na prvých 5 prednáškach. Nepoužívaj príkaz import
ani indexovanie pomocou hranatých zátvoriek [ ]
.
Súbor riesenie.py
odovzdávaj na úlohový server https://list.fmph.uniba.sk/ najneskôr do 23:00 23. októbra, kde ho môžeš nechať otestovať. Odovzdať projekt a aj ho testovať môžeš ľubovoľný počet krát. Toto je len tréningové, štartovacie zadanie, môžeš zaň získať 1 bod.