Funkcije

Funkcije smo si doslej predstavljali kot škatlice: nekaj gre noter (temu smo in bomo rekli argument), nekaj pride ven (temu se reče rezultat funkcije), vmes pa se lahko še kaj opaznega dogaja, recimo izpisuje. Primer funkcije, ki je počela vse to, je input: kot argument smo ji povedali, kaj naj vpraša uporabnika; kot rezultat je vrnila, kar je vtipkal uporabnik; vmes se je zgodilo to, da je funkcija nekaj vprašala uporabnika in počakala, da je le-ta odgovoril. Druga funkcija, ki smo jo srečali, je bila sqrt, ki dobi kot argument neko število in vrne njegov koren. Vmes se ne dogaja nič opaznega, funkcija le "neopazno" naredi, kar mora narediti.

S tem, kako delujejo funkcije in kako kaj naredijo, se doslej nismo ukvarjali. Te stvari so za nas napisali drugi (hvala, hvala) in mi jih lahko uporabljamo, ne da bi nas vznemirjalo vprašanje, kako so napisane. S tem, kako so napisane funkcije, ki so jih naredili drugi, se tudi v prihodnje ne bomo ukvarjali. Pač pa se bomo danes naučili pisati svoje.

Popolna števila

Število je popolno, če je enako vsoti svojih deliteljev. 28 je deljivo z 1, 2, 4, 7 in 14 ter je popolno, saj je 1+2+4+7+14 ravno 28. Napišimo program, ki sestavi seznam vseh popolnih števil do 1000.

Tokrat se dela ne bomo lotili tako, kot smo se ga ponavadi, temveč se bomo ob tem primeru naučili pisati funkcije.

Delitelji števila

Znamo napisati program, ki sestavi seznam vseh deliteljev nekega števila n?

Ne čisto, ker smo sezname doslej vedno sestavili ročno, tako da smo napisali, recimo, s = [5, 1, 6, 7, 2]. Seznamov nikoli ni naredil program sam. To se naučimo kar mimogrede: če imamo nek seznam s in bi radi vanj dodali število 42, napišemo s.append(42). Zdaj pa bomo menda znali:

n = int(input("Vnesi število: ")) s = [] for i in range(1, n): if n % i == 0: s.append(i) print(s)

Najprej naredimo prazen seznam, nato gremo prek vseh števil od 1 do n in če število deli n, ga dodamo v s.

Kaj ne bi bilo lepo, če bi imel Python kar funkcijo delitelji, ki bi jo lahko uporabili? Potem bi lahko napisali kar

stevilka = int(input("Vnesi število: ")) s = delitelji(stevilka) print(s)

Kot je povsem pravilno predlagal profesor Franc Oblak: česar ni, se pa nardi (ter v sredo polinoma, ki se ga na noben način ni dalo razcepiti, dodal + x - x in polinom je šel na faktorje kot toplo maslo). Napišimo si takšno funkcijo, da jo bomo lahko klicali.

def delitelji(n): s = [] for i in range(1, n): if n % i == 0: s.append(i) return s

Definicijo funkcije začnemo z def; to je rezervirana beseda, ki pomeni, da to, kar sledi, ni "program, ki ga je treba takoj izvesti", temveč funkcija. Z drugimi besedami, def delitelji pomeni: "kadar bo kdo poklical funkcijo delitelji, naredi naslednje:".

Imenu sledijo oklepaji, v katerih navedemo imena argumentov funkcije. V našem primeru bo funkcija zahtevala en argument. Torej, ta, ki bo poklical funkcijo, bo moral v oklepaje napisati eno reč (upamo, da bo napisal število, sicer pa naj si sam pripiše posledice).

Tista reč, ki jo bomo ob klicu funkcije podali kot argument, se bo znotraj funkcije pojavila kot spremenljivka z imenom n. Takšno ime smo namreč uporabili v prvi vrstici, v "glavi" funkcije. Vrednosti ji ne bomo priredili: ko bo nekdo poklical funkcijo, bo Python tej spremenljivki kar sam od sebe priredil vrednost, ki jo bo "klicatelj" napisal kot argument funkcije. Če torej nekdo pokliče

s = delitelji(35) bo imel n vrednost 35 in če pokliče s = delitelji(13) bo imel n vrednost 13. n ima vrednost argumenta.

Za def delitelji(n) sledi dvopičje in zamik. Vse, kar sledi takole zamaknjeno, je koda funkcije. Kaj mora narediti le-ta? No, tisto, kar pač dela funkcija: sestaviti seznam deliteljev n. To pa ne le znamo, temveč smo celo ravno prejle naredili in lahko le skopiramo.

Na koncu (ali tudi že kje vmes - bomo že videli primer) funkcija pove, kaj naj klicatelj dobi kot rezultat. To stori s stavkom return s.

Geometrija

Napišimo funkcijo, ki dobi kot argument dolžine stranic trikotnika in vrne njegovo ploščino. Ta funkcija bo imela tri argumente; poimenujmo jih a, b in c.

from math import * def ploscina_trikotnika(a, b, c): s = (a + b + c) / 2 return sqrt(s * (s - a) * (s - b) * (s - c))

Ko smo pri tem, napišimo še funkcijo funkcijo za obseg trikotnika ter za obseg in ploščino kroga.

from math import * def obseg_trikotnika(a, b, c): return a + b + c def ploscina_kroga(r): return pi * r ** 2 def obseg_kroga(r): return 2 * pi * r

Vsota števil v seznamu

Zdaj napišimo drugo funkcijo: funkcijo, ki dobi seznam števil in izračuna njihovo vsoto.

def vsota(s): vsota = 0 for e in s: vsota += e return vsota

Funkcija ima spet en argument, tokrat smo ga poimenovali s. Ta argument bo seznam števil, ki jih je potrebno sešteti. Funkcija gre - z zanko for - prek tega seznama in sešteva, kot smo počeli že včeraj v trgovini. Na koncu rezultata ne izpiše (print), kot smo delali doslej, temveč ga vrne (return).

(Mimogrede povejmo še, da nam funkcije vsota ne bi bilo potrebno napisati, saj obstaja: imenuje se sum.)

Popolno število

Vrnimo se k popolnim številom. Rekli smo, da je število popolno, če je enako vsoti svojih deliteljev. Lahko bi torej rekli

stevilo = int(input("Vnesi število: ")) d = delitelji(stevilo) v = vsota(d) if v == d: print("Število", stevilo, "je popolno") else: print("Število", stevilo, "ni popolno")

vendar ne bomo. Napisali bomo funkcijo, ki vrne True, če je število popolno in False, če ni.

def popolno(n): d = delitelji(n) v = vsota(d) return v == n

Pazite, tudi tule nismo pisali

def popolno(n): d = delitelji(n) v = vsota(d) if v == n: return True else: return False

Lahko bi, vendar bi bilo smešno. Pač pa lahko funkcijo še skrajšamo (in navadno bi jo tudi res), takole

def popolno(n): return vsota(delitelji(n)) == n

Končno ostane še program, ki bo z uporabo gornje funkcije sestavil seznam vseh popolnih števil manjših od 1000. V njem z zanko for preštejemo do 1000, za vsako število posebej preverimo, ali je popolno in če je, ga dodamo na seznam.

s = [] for i in range(1, 1001): if popolno(i): s.append(i) print(s)

Klici funkcij

Najprej zberimo vse skupaj:

def delitelji(n): s = [] for i in range(1, n): if n % i == 0: s.append(i) return s def vsota(s): v = 0 for e in s: v += e return v def popolno(n): d = delitelji(n) v = vsota(d) return v == n s = [] for i in range(1, 1001): if popolno(i): s.append(i) print(s)

Kako se izvaja tako napisan program? Malo drugače, kot smo vajeni. Začetek programa - vse do mesta s = [], so definicije funkcij. Python tega dela programa ne izvede, le zapomni si funkciji, da ju bo kasneje lahko poklical. Stvari se začnejo zares dogajati šele, pri s = []. Ko program pride do klica funkcije popolno, skoči v to funkcijo -- vendar si zapomni, odkod je skočil, tako da se bo kasneje lahko vrnil na to mesto.

Prav. Zdaj smo v funkciji popolno in n je neka številka (najprej 1, naslednjič bo 2 in tako naprej). Že takoj, v prvi vrstici skoči izvajanje v funkcijo delitelji. Ta sestavi seznam deliteljev in ga vrne - tistemu, ki jo je poklical, funkciji popolno. Nato se nadaljuje izvajanje funkcije popolno: ta v naslednji vrsti pokliče funkcijo vsota. Funkcija vsota izračuna in vrne vsoto. Spet smo v funkciji popolno, ki izračuna vrednost izraza v == n; vrednost izraza je True ali False. Funkcija popolno ga vrne tistemu, ki jo je klical, se pravi oni zanki na koncu skripte. Če je rezultat True, se bo število izpisalo, sicer pač ne.

Rezultat sredi funkcije

Napišimo funkcijo, ki pove, ali je dano število praštevilo. Vrnila bo True (je) ali False (ni).

def prastevilo(n): for i in range(2, n): if n % i == 0: return False return True

Prvi return je znotraj stavka if. Če odkrijemo, da kakšno število med 2 in n-1 deli n (se pravi, če je ostanek po deljenju n z i enak 0), dano število ni praštevilo in vrnemo False. S tem se izvajanje funkcije prekine, funkcija vrne rezultat in konec. Nobenega break ali česa podobnega ne potrebujemo. return vedno konča izvajanje funkcije. Do drugega returna tako pridemo le, če se ni izvedel prvi return. To pa seveda pomeni, da ni bilo nobenega števila, ki bi delilo dano število n.

Zadnja sprememba: petek, 17. oktober 2014, 16.47