comment up next how to englisch dex

5. Funkce produktivní

  1. Výstupní hodnoty
  2. Rozvoj programu
  3. Složené funkce
  4. Booleovské funkce
  5. Typ 'funkce'
  6. Styl programu
  7. Trojité uvozovky
  8. Testování s 'doctestem'
  9. Glosář
  10. Cvičení

5.1 Výstupní hodnoty

Vestavěné funkce, které jsme zatím používali, jako abs, pow a max tvoří výsledky. Volání každé z těchto funkcí generuje hodnotu, kterou obvykle přiřadíme k proměnné nebo použijeme jako součást výrazu:

biggest = max(3,7,2,5)
x = abs(3-11) + 10

V kapitole 3 nám vracela hodnotu ta funkce, která obsahovala příkaz print. Jak to dopadne, když tento příkaz do funkce nezadáme? Zkusme si to na příkladech:

>>> def hum(a,b):  # zde 'print' máme 
...     print a+b
...
>>> hum(2,3)
5                    # a máme výstup

>>> def lum(a,b):   # zde 'print' nemáme
...     a+b    
>>> lum(2,3)
>>>                  # a výstup také ne
>>> x = lum(2,3)
>>> print x
None                  # ani zde ne

Bez výslovného příkazu na nás funkce nemluví, v lepším případě krčí rameny (viz None).

V této kapitole se seznámíme s funkcemi, které vrací hodnotu příkazem return. Než nás napadne lepší označení, budeme jim říkat funkce produktivní (fruitful functions), neboli funkce s výstupní hodnotou. Prvním příkladem bude fce area, která vrátí plochu kruhu zadaného poloměrem:

def area(radius):
  temp = 3.14159 * radius**2
  return temp

Příkaz return jsme již viděli, ale ve funkci výnosné zahrnuje return i výstupní hodnotu. Říká doslova: "Okamžitě z této funkce vystup a následující výraz použij jako výstupní hodnotu". Onen výraz může být libovolně složitý, takže můžeme zapsat stručněji:

def area(radius):
  return 3.14159 * radius**2

Na druhé straně, dočasné proměnné jako je temp ulehčují vyhledávání chyb.

Někdy je užitečné mít více příkazů return, v každé větvi podmínky jeden. Poznali jsme již vestavěnou fci abs; nyní si napíšeme vlastní:

def absolute_value(x):
  if x < 0:
    return -x
  else:
    return x

Protože tyto příkazy jsou v alternativní podmínce, provede se pouze jeden. Jakmile se provede, funkce končí bez provedení následných příkazů.

Naši funkci můžeme zapsat také stručněji. Místo příkazu else dáme přímo příkaz return.

def absoluteValue(x):
  if x < 0:
    return -x
  return x

Přesvědčme se, že tato fce pracuje stejně jako ta předchozí.

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

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

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

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

>>> print absoluteValue(0)
None

None je jedinečná hodnota typu NoneType:

>>> type(None)
<type 'NoneType' >

5.2 Rozvoj programu

Nyní bychom bychom měli být schopni poznat pouhým pohledem na zápis funkce co má provádět. Pokud jsme prováděli cvičení, sami jsme několid menších fcí napsali. Při psaní větších funkcí nám mohou nastat potíže kvůli významovým chybám a chybám při běhu programu.

Abychom se vyrovnali s rostoucí složitostí programů seznámíme se s technikou, zvanou přírůstkový rozvoj. Jeho cílem je vyloučení dlouhých seancí při odstraňování chyb tím, že se postupně přidávají a testují krátké úseky kódu.

Jako příklad předpokládejme, že chceme nalézt vzdálenost dvou bodů, daných souřadnicemi (x1, y1) a (x2, y2). Vzdálenost podle Pythagorovy věty je:

Nejprve musíme uvážit jak by měla funkce distance vypadat. Jinými slovy, jaké budou vstupy (parametry) a jaký bude výstup (hodnota return)?

V našem případě musíme na vstupu zadat dva body, reprezentované čtyřmi parametry. Výstupní hodnotou bude vzdálenost s hodnotou float.

Již můžeme psát obrys funkce:

def distance(x1, y1, x2, y2):
  return 0.0

Je zřejmé, že tato verze žádnou vzdálenost nepočítá, neboť vždycky vrátí nulu. Je ale skladebně (syntakticky) správná, poběží a můžeme jí testovat před tím než ji zkomplikujeme.

Abychom ji otestovali, zavoláme ji pro jednoduché hodnoty:

>>> distance(1, 2, 4, 6)
0.0

Hodnoty jsme vybrali tak, aby vodorovná vzdálenost bodů byla 3, svislá 4 a výsledná vzdálenost 5 (což je přepona pravoúhlého trojúhelníka). Při testování funkce je užitečné znát správný výsledek předem.

V této chvíli jsme se přesvědčili, že funkce je syntakticky správná a můžeme začít přidávat řádky kódu. Po každé malé změně funkci znovu otestujeme. Objeví-li se v kterémkoli místě chyba, budeme vědět kde musí být – v posledním přidaném řádku.

Prvním logickým krokem ve výpočtu bude nalézt rozdíly x2-x1 a y2-y1. Tyto hodnoty uložíme do dočasných proměnných dx,dy a vytiskneme je.

def distance(x1, y1, x2, y2):
  dx = x2 - x1
  dy = y2 - y1
  print "dx is", dx
  print "dy is", dy
  return 0.0

Je-li funkce v pořádku, měly by výsledky být 3 a 4. Jestliže ano, ověřili jsme si, že náš dílčí program pracuje správně. Pokud ne, potřebujeme prověřit jenom několi málo řádků.

Nyní sečteme součet čtverců pro dx a dy.

def distance(x1, y1, x2, y2):
  dx = x2 - x1
  dy = y2 - y1
  dsquared = dx**2 + dy**2
  print "dsquared is: ", dsquared
  return 0.0

Všimněme si, že jsme odstranili příkaz print, který jsme zapsali v předchozím kroku. Takové části kódu říkáme lešení, protože pomáhá při sestavení programu, ale není součástí finálního výsledku.

Opět necháme proběhnout program a zkontrolujeme výstup (což by mělo být 25).

Konečně, s použitím zlomkového exponentu 0.5 pro nalezení odmocniny, můžeme spočítat výsledek:

def distance(x1, y1, x2, y2):
  dx = x2 - x1
  dy = y2 - y1
  dsquared = dx**2 + dy**2
  result = dsquared**0.5
  return result

Pokud nám to chodí správně, jsme hotovi.

Když začínáme, měli bychom přidávat jen po jednom či dvou řádcích. Po získání jisté zkušenosti můžeme přidávat po větších dávkách. Přírůstkový rozvoj nám v každém případě ušetří mnoho času při odstraňování chyb.

Klíčové aspekty tohoto postupu jsou:

  1. Začneme s chodícím programem a děláme malé přírůstkové změny. V každém okamžiku víme přesně kde může být případná chyba.
  2. Použijeme dočasných proměnných k uložení mezivýsledků; tak je můžeme vytisknout a zkontrolovat.
  3. Když program pracuje bezchybně, můžeme odstranit nepotřebné lešení, případně uspořádat některé příkazy do složených výrazů, ovšem tak, aby program zůstal dostatečně přehledný.

5.3 Složené funkce

Padla již zmínka, že jedna funkce může volat druhou. Této schopnosti říkáme propojení nebo složení (composition) funkcí.

Jako příklad si napíšeme funkci, která pro zadané dva body (střed kružnice a bod na obvodu) vypočítá plochu kruhu.

Střed kružnice uložíme do proměnných xc,yc, bod obvodu do xp,yp. Prvním krokem bude určení poloměru kružnice, což je vzdálenost mezi oběma body. Naštěstí jsme právě napsali funkci distance, která to umí, takže ji můžeme hned použít:

radius = distance(xc, yc, xp, yp)

V druhém kroku určíme plochu kruhu pro daný poloměr a zobrazíme ji.

result = area(radius)
return result

Když to celé zabalíme do jedné funkce, dostaneme:

def area2(xc, yc, xp, yp):
  radius = distance(xc, yc, xp, yp)
  result = area(radius)
  return result

Funkci jsme pojmenovali area2, abychom ji odlišili od funkce area, definované dříve. V jednom modulu můžeme mít jenom jednu funkci daného jména.

Dočasné proměnné radius a result jsou užitečné jenom pro rozvoj programu a vychytání chyb. Jakmile nám program pracuje správně, můžeme jej zestručnit spojením funkcí.

def area2(xc, yc, xp, yp):
  return area(distance(xc, yc, xp, yp))

5.4 Booleovské funkce

Funkce mohou vracet i booleovské hodnoty, což je často vhodné pro ukrytí složitých ověřování uvnitř funkcí. Například:

def is_divisible(x, y):
  if x % y == 0:
    return True
  else:
    return False 

Jméno této funkce je is_divisible (je_dělitelné). Je obvyklé dávat booleovským funkcím jména, která zní jako otázka "ano či ne". Funkce is_divisible vrací buď True nebo False podle toho, zda x je či není dělitelné y.

Funkci můžeme ještě zjednodušit využitím faktu, že podmínka příkazu if je sama booleovským výrazem. Výsledek získáme přímo i po vypuštění příkazu if:

def is_divisible(x, y):
  return x % y == 0

Následující seance předvádí novou funkci v akci:

>>>is_divisible(6, 4)
False
>>>is_divisible(6, 3)
True

Booelovské funkce jsou často používány v podmíněných příkazech:

if is_divisible(x, y):
  print "x is divisible by y"
else:
  print "x is not divisible by y"

Mohlo by být lákavé napsat výraz ve tvaru:

if is_divisible(x, y) == True:
  ...

Avšak toto extra srovnání je nadbytečné.

5.5 Typ 'funkce'

Funkce ja dalším typem Pythonu, jako dosud poznané typy int, float, str, bool  a  NoneType.

>>> def func():
...   return "fce func was called"
... 
>>> type (func)
<type 'function'>
>>>

Stejně jako ostatní typy i funkce mohou být zadány jako argumenty jiným funkcím.

def f(n):
  return 3*n - 6
def g(n):
  return 5*n + 2
def h(n):
  return -2*n + 17
def doto(value, fun):
  return fun(value)

print doto(7, f)
print doto(7, g)
print doto(7, h)

Funkce doto je volána třikrát pro stejný číselný argument a pro tři různá jména funkcí. V těle fce doto se říká, co má být jejím výstupem. Na konci skriptu jsou tři volání fce doto. Výstupem bude toto:

>>>
15
37
3
>>>

Co se stane, když jméno argumentu fun nahradíme vlastním křestním jménem?

5.6 Styl programu

Dobrá čitelnost programu je pro programátora velmi důležitá, protože programy jsou častěji čteny a upravovány než psány. Všechny příklady kódů v této knize jsou psány ve shodě s manuálem Python Enhancement Proposal 8 (PEP 8), v němž jsou uvedeny pokyny pro stylové úpravy kódu.

Až naše programy budou rozsáhlejší, budou i naše potřeby stylu naléhavější ale několik pokynů bude užitečných už teď:

5.7 Trojité uvozovky

Kromě jednoduchých a dvojitých uvozovek, které jsme poprvé viděli v Kapitole 2 při označení řetězců, má Python také řetězce s trojitými uvozovkami. Tyto uvozovky mohou být tvořeny jednoduchými i dvojitými uvozovkami:

>>> type ("""This is a triple quoted string
 using 3 double quotes.""")
<type 'str'>
>>> type ('''This is a triple quoted string
 using 3 single quotes.''')
<type 'str'>
>>>

Uvnitř řetězce s trojitými uvozovkami mohou být uvozovky jednoduché i dvojité:

>>> print ''' "Ach ne", zvolala, "Benovo kolo je rozbite" '''
"Ach ne", zvolala, "Benovo kolo je rozbite"
>>> 

Konečně, řetězce s trojitými uvozovkami mohou zabírat i více řádků:

>>> message = """ This message will
... span several
... lines. """
>>> 

Tento tvar uvozovek se používá pro dokumentační řetězce (docstrings). Docstring je řetězec, umístěný jako první příkaz v modulu, funkci, třídě nebo v definici metody. Pojmy třída a metoda budou probrány později, modul je v podstatě soubor s koncovkou .py.

5.8 Testování s 'doctestem'

V dnešní době je při rozvoji programu velmi používané automatické testování krátkých úseků zdrojového kódu.

Pro toto testování má Python vestavěný modul doctest. Zkoumané vzorky kódu se umístí do dokumentačního řetězce na první řádce skriptu nebo těla funkce. V každém vzorku je na prvním řádku volání fce s argumentem, na druhém řádku je očekávaná odezva.

Za docstringem se umístí testovaný kód. Na konci skriptu nutno umístit záhadný příkaz, který importuje modul doctest a spustí testování. Ukázku si uložme do souboru myfunctions.py:

def is_divisible_by_2_or_5(n):
  """
   >>> is_divisible_by_2_or_5(8)
   True
   >>> is_divisible_by_2_or_5(7)
   false
   >>> is_divisible_by_2_or_5(5)
   True
   >>> is_divisible_by_2_or_5(9)
   false
  """
   return n%2 == 0 or n%5 == 0

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

Modul doctest prověří zadanou fci pro každý argument, uvedený v dokumentačním řetězci a každý dílčí výstup porovná se zadanými hodnotami.

Zjistí-li shodu, neděje se nic. Pokud narazí na rozpor, spustí rozsáhlé chybové hlášení.

Vyzkoušejme si to na našem příkladě, když záměrně pošpatníme funkci tak, že druhou rovnost (==) nahradíme nerovností (!=).

5.9 Glosář

funkce produktivní (fruitful function)
Funkce, která vrací hodnotu.
výstupní hodnota (return value)
Hodnota, která je výsledkem volání funkce.
prozatimní proměnná (temporary variable)
Proměnná použitá k uložení přechodné hodnoty u složitějšího výpočtu.
mrtvý kód (dead code)
Část programu, která se nikdy neprovede, často proto, že se nacházi za příkazem return
None
Speciální hodnota v Pythonu, vracená funkcemi, které nemají příkaz return nebo jej mají bez argumentu. None je jediná hodnota typu NoneType
přírůstkový rozvoj (incremental development)
Postupné rozšiřování programu se záměrem testovat najednou jenom menší objem kódu.
lešení (scaffolding)
Kód, použitý při rozvoji programu, který není součástí konečné verze.
booleovská funkce (boolean function)
Funkce, která vrací booleovskou hodnotu.
složení funkcí (composition of functions)
Volání jedné funkce z těla druhé, nebo použití výstupní hodnoty jedné funkce jako argument při volání druhé.
 dílčí testování (unit testing)
Automatická procedura používaná k ověření správné činnosti jednotlivých úseků kódu. V Pythonu se pro tento účel používá vestavěná metoda doctest.

5.10 Cvičení

Všechna cvičení budou postupně přidávána do souboru se jménem ch05.py. Na konec totoho skriptu se umístí následující kód:

if __name__ == '__main__':
   import doctest
   doctest.testmod()
Po zapsání každého cvičení spustíme skipt, abychom se přesvědčili, že naše nová funkce projde testem.

  1. Napiš funkci compare, která vrací 1 pro x>y, 0 pro x==y a -1 pro x<y.
    def compare (a, b):
      """
       >>> compare(5,4)
       1
       >>> compare(7,7)
       0
       >>> compare(2,3)
       -1
      """
       # zde napis svou fci
    
  2. Použij přírůstkový rozvoj k vytvoření funkce hypotenuse, která vrací délku přepony pravoúhlého trojúhelníka pro zadané délky odvěsen. Použij lešení.
    def hypotenuse (a,b):
      """
       >>> hypotenuse(3,4)
       5.0
       >>> hypotenuse(12,5)
       13.0
       >>> hypotenuse(7,24)
       25.0
      """
       # zde napis svou fci
    
  3. Napiš funkci slope(x1,y1,x2,y2), která vrací úhel sklonu přímky procházející body (x1,y1) a (x2,y2). Funkce musí vyhovět následujícím testům:
    def slope (x1,y1,x2,y2):
      """
       >>> slope(5,3,4,2)
       1.0
       >>> slope(1,2,3,2)
       0.0
       >>> slope(1,4,1,2)
       0.5
      """
       # zde napis svou fci
    
    Funkci slope použij v další funkci intercept(x1,y1,z2,y2), která vrací ypsilonovou pořadnici přímky, procházející bodem (x1,y1) a (x2,y2).
    def intercept (x1,y1,x2,y2):
      """
       >>> intercept(1,6,3,12)
       3.0
       >>> intercept(6,1,1,6)
       7.0
       >>> intercept(4,6,12,8)
       5.0
      """
       # zde napis svou fci
    
  4. Napiš funkci is_even(n), která přijme celé číslo jako argument a vrací True, je-li číslo sudé a False, je-li liché. Vytvoř vlastní doctesty.
  5. Nyní napiš funkci is_odd(n), která vrací True, je-li n liché a False, je-li liché. Uprav fci tak, že k určení sudosti / lichosti volá fci is_even. Vytvoř vlastní doctesty.
  6. Napiš funkci is_factor(f,n) se dvěma parametry f a n, která vrátí True, je-li f dělitelem n a False, není-li.
    def is_factor (f,n):
      """
       >>> is_factor(3,12)
       True
       >>> is_factor(5,12)
       False
       >>> is_factor(2,14)
       True
      """
       # zde napis svou fci
    
  7. Napiš funkci is_multiple(m,n) se dvěma parametry m a n, která vrátí True, je-li m násobkem n a False, není-li.
    def is_multiple( (m,n):
      """
       >>> is_multiple(12,3)
       True
       >>> is_multiple(12,4)
       True
       >>> is_multiple(12,5)
       False
      """
       # zde napis svou fci
    
    Šlo by použít fci is_factor při definici fce is_multiple?
  8. Napiš funkci f2c(t), která převede teplotu ve stupních Fahenheitových na stupně Celsiovy True a vrátí ji jako celé číslo. Použiješ vestavěnou fci round(n).
    def f2c( (t):
      """
       >>> f2c(212)
       100
       >>> f2c(32)
       0
       >>> g2c(-40)
       -40
       >>> g2c(37)
       3
      """
       # zde napis svou fci
    
  9. Napiš funkci c2f(t), která převede teplotu ve stupních Celsiových na stupně Fahrenheitovy.

comment up next how to englisch dex