Funkcije ======== U svijetu programiranja važi **DRY** (eng. *Don't Repeat Yourself*) princip. Ovaj princip znači da uvijek, kada je moguće, treba izbjegavati ponavljanje dijelova koda. Međutim, česte su situacije u kojima je ponavljanje segmenata koda ili određenih funkcionalnosti neophodno sa logičke strane programa. Na primjer, više puta unutar programa treba uraditi konverziju novca iz jedne valute u drugu. Samim tim, postavlja se pitanje: na koji način se može izbjeći ponavljanje koda? Odgovor na to pitanje je upotreba **funkcija**. Funkcijama se dijelovi koda grupišu u cjeline, te one omogćavaju razbijanje složenih programerskih zadataka na manje, razumljivije dijelove, koji su lakši za održavanje. Njihova osnovna svrha je olakšati kreiranje, održavanje i ponovnu upotrebu koda. Naravno, funkcije značajno olakšavaju primjenu DRY pravila. Umjesto da se iste sekvence koda pišu više puta, funkcije omogućavaju definisanje logike jednom, a zatim upotrebu onoliko puta koliko je potrebno i na mjestima gdje je to potrebno. ## Koncept "Crne kutije" Programeri se sa funkcijama susreću već kroz prve programe. Ranije korištene naredbe `print()` i `input()` su u konačnici funkcije, koje omogućavaju ispis podataka i unos korisničkih informacija. Za korisnika, ali i za programera koji koristi funkcije, one se ponašaju kao crne kutije – poznato je šta se unosi i šta se može očekivati kao izlaz, dok način na koji funkcija postiže rezultat ostaje često nepoznat. Na primjer, `print()` prima podatke kao ulaz i prikazuje ih na ekranu, dok `input()` omogućava unos informacija koje program dalje koristi i vraća korisniku na raspolaganje za rad. Upravo ovakva apstrakcija pojednostavljuje rad i omogućava fokusiranje na ono što funkcija treba da uradi, umjesto na detalje njenog unutrašnjeg rada. Koncept rada funkcija predstavljen je na slici ispod. ```mermaid flowchart LR A[Ulazni podaci]--> B[Funkcija] B --> C[Izlazni podaci] style B fill:#000,stroke:#333,stroke-width:2px,color:#fff,font-weight:bold style A fill:#e3f2fd,stroke:#2196f3,stroke-width:2px style C fill:#e3f2fd,stroke:#2196f3,stroke-width:2px ``` ## Prednosti upotrebe funkcija Kako je ranije navedeno, funkcije donose brojne prednosti u razvoju softverskih rješenja. Smanjenje ponavljanja koda kroz podršku DRY principu je već spomenuto. Od ostalih prednosti upotrebe funkcija, vrijedi istaknuti: * Unapređenje modularnosti programa; * Lakše testiranje i održavanje; * Mogućnost ponovne upotrebe koda; * Podrška timskom razvoju softvera. ### Unapređenje modularnosti programa Modularnost u programiranju podrazumijeva razbijanje kompleksnih zadataka na manje, nezavisne dijelove – module. Modul se može definisati kao funkcionalna cjelina unutar programa, koja ima jasno određenu svrhu i koja se može koristiti, testirati i mijenjati bez uticaja na druge dijelove sistema. U kontekstu funkcija, svaki modul odgovara pojedinačnoj funkciji koja obavlja specifičan zadatak. Funkcije značajno doprinose modularnosti programa, jer omogućavaju programerima da se fokusiraju na pojedinačne dijelove koda umjesto na cijeli sistem odjednom. Ovo ne samo da olakšava razumijevanje koda, već i pojednostavljuje održavanje. Pojedinačni moduli mogu se jednostavno testirati, prilagoditi ili zamijeniti, čime se povećava fleksibilnost i skalabilnost programa. ### Lakše testiranje i održavanje Funkcije omogućavaju da se kod testira u izolovanim jedinicama. Svaka funkcija može biti testirana zasebno, što značajno smanjuje kompleksnost pri pronalaženju grešaka. Ako se problem pojavi, greška se obično može pratiti do određene funkcije, izolovati od ostatka koda i debagirati. Sve to ubrzava proces dijagnostike i popravke. Pored toga, funkcije omogućavaju jednostavnije prilagodbe i proširenja programa, jer se izmjene mogu vršiti lokalno u funkcijama bez uticaja na ostatak koda. ### Mogućnost ponovne upotrebe koda Jednom napisane funkcije mogu se koristiti u različitim dijelovima istog programa ili čak u potpuno drugim projektima. Ovo ne samo da štedi vrijeme i trud, već osigurava dosljednost u načinu na koji se određeni zadatak obavlja. ### Podrška timskom razvoju softvera U velikim projektima, gdje više programera radi na različitim dijelovima sistema, funkcije omogućavaju jasnu podjelu zadataka. Svaki član tima može raditi na razvoju i testiranju svojih funkcija, znajući kako će se one uklopiti u cjelinu. Funkcije sa jasno definisanim ulazima i izlazima olakšavaju integraciju različitih dijelova sistema, smanjujući mogućnost nesporazuma i grešaka. ## Kako se definišu funkcije? Funkcije u Pythonu definišu se korištenjem ključne riječi `def`, koja označava početak definicije funkcije. Svaka funkcija ima naziv, opcionalne parametre, te tijelo u kojem se implementira logika funkcije. U narednom isječku koda prikazana je osnovna sintaksa funkcije u Pythonu. ```python= def naziv_funkcije(parametri): tijelo_funkcije ``` Ova struktura omogućava kreiranje funkcija koje mogu obavljati specifične zadatke unutar programa. Svaki dio ove sintakse ima svoj značaj: - **`def`**: Ključna riječ koja označava početak definicije funkcije. Bez nje Python neće prepoznati da se definiše funkcija. - **Naziv funkcije**: Koristi se za identifikaciju funkcije i treba biti smislen kako bi jasno opisivao njenu svrhu. Naziv mora poštovati pravila imenovanja varijabli u Pythonu. - **Parametri**: Ulazne vrijednosti koje funkcija koristi za izvođenje zadatka. Parametri se navode unutar zagrada, odvojeni zarezima, i mogu biti opcionalni. - **Tijelo funkcije**: Sadrži instrukcije koje funkcija izvršava. Mora biti uvučeno kako bi Python prepoznao da pripada funkciji. Tijelo funkcije može biti izuzetno kompleksno, te izvršavati niz zadataka, iako je ustaljena praksa da jedna funkcija obavlja manji niz zadataka, radi bolje čitljivosti. ## Poziv funkcije U nastavku je prikazana osnovna funkcija koja služi za ispis nekoliko poruka na ekran. Funkcija samo izvršava ono što je napisano unutar njenog tijela i nema nikakve ulazne ili izlazne vrijednosti. ```python= def prikazi_poruku(): print("Dobrodošli u svijet funkcija!") print("Funkcije čine vaš kod organizovanijim.") print("Korištenje funkcija pojednostavljuje programiranje.") ``` Funkcija `prikazi_poruku` sadrži tri naredbe `print()` koje ispisuju poruke. Iako funkcija izgleda jednostavno, sama definicija ne pokreće njen kod. Definisanje funkcije samo omogućava da se njeno ime kasnije koristi za pozivanje. Funkcija mora biti pozvana da bi se njen kod izvršio. Ovo se radi jednostavno pisanjem imena funkcije i parom zagrada `()`: ```python= prikazi_poruku() ``` <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Dobrodošli u svijet funkcija! Funkcije čine vaš kod organizovanijim. Korištenje funkcija pojednostavljuje programiranje. </pre> Kada se funkcija pozove, Python pronalazi njenu definiciju i izvršava njen kod. U ovom primjeru, sve tri poruke unutar funkcije biće ispisane na ekran. ### Poziv funkcije više puta Funkcije se mogu pozvati više puta u programu. Svaki put kada se pozovu, njihov kod se izvršava iznova. ```python= prikazi_poruku() prikazi_poruku() prikazi_poruku() ``` <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Dobrodošli u svijet funkcija! Funkcije čine vaš kod organizovanijim. Korištenje funkcija pojednostavljuje programiranje. Dobrodošli u svijet funkcija! Funkcije čine vaš kod organizovanijim. Korištenje funkcija pojednostavljuje programiranje. Dobrodošli u svijet funkcija! Funkcije čine vaš kod organizovanijim. Korištenje funkcija pojednostavljuje programiranje. </pre> Svaki put kada se funkcija `prikazi_poruku` pozove, iste naredbe `print` unutar njenog tijela biće izvršene. To pokazuje kako funkcije omogućavaju ponovnu upotrebu koda, čineći ga kraćim i lakšim za održavanje. :::info **Mijenjanje koda je mnogo jednostavnije ukoliko je isti raspoređen u funkcije.** Na primjer, promjena teksta koji se ispisuje postiže se jednostavnom izmjenom unutar definicije funkcije. Bez upotrebe funkcija, bilo bi potrebno pronaći i izmijeniti sve dijelove koda gdje se tekst ponavlja, što u većim programima može značiti desetine ili čak stotine izmjena, umjesto jedne. ::: ## Parametri funkcija i povratni tipovi Funkcije u Pythonu mogu se klasifikovati prema načinu na koji primaju ulazne podatke (parametre) i prema tome da li vraćaju rezultat (povratni tip). Ova fleksibilnost omogućava funkcijama da se prilagode širokom spektru programerskih potreba. :::success **Parametar** predstavlja ulaznu vrijednost koja se prosljeđuje funkciji prilikom njenog poziva. Parametri omogućavaju funkcijama da obrađuju različite podatke bez potrebe za izmjenama u njihovoj definiciji. Na taj način funkcije postaju prilagodljive i mogu se koristiti u različitim situacijama. ::: :::success **Povratni tip** je vrijednost koju funkcija vraća kao rezultat svog izvršavanja. Povratni tip omogućava daljnju upotrebu rezultata funkcije u ostatku programa, bilo da se koristi za izračune, ispis, ili druge operacije. Ukoliko funkcija nema povratni tip, podrazumijeva se da vraća `None`. ::: U nastavku se opisuje svaki od ovih tipova funkcija, uz primjere koji ilustruju njihovu upotrebu. ### Funkcije bez parametara i bez povratnih vrijednosti Funkcije bez parametara ne primaju nikakve ulazne podatke. Ove funkcije obično izvode radnju koja ne zavisi od vanjskih vrijednosti (ili koriste globalne podatke definisane u programu, što će biti naknadno opisano). ```python= def ispisi(): print("Učimo funkcije!") ``` :::warning Kako je i ranije navedeno, sama definicija funkcije neće izvršiti funkciju. Da bi se funkcija izvršila, potrebno je pozvati istu. ```python= ispisi() ``` ::: Kada se pozove funkcija `ispisi()`, jednostavno se izvršava njen kod bez potrebe za prosljeđivanjem vrijednosti. <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Učimo funkcije! </pre> ### Funkcije sa parametrima i bez povratnih vrijednosti Funkcije sa parametrima primaju ulazne podatke koji se koriste za izvršavanje određene radnje. Parametri omogućavaju prilagodljivost funkcija, jer vrijednosti mogu varirati pri svakom pozivu. U nastavku je primjer funkcija koja prihvata dva broja, te ispisuje koji je od njih veći, ukoliko takav postoji, ili ispisuje prikladnu poruku ukoliko su brojevi jednaki. ```python= def veci_broj(broj1, broj2): if broj1 > broj2: print("Veći je prvi broj") elif broj1 < broj2: print("Veći je drugi broj") else: print("Jednaki su") ``` Ovdje funkcija `veci_broj` prima dva broja kao parametre i ispisuje ranije definisani rezultat. Poziv funkcije, kao i odgovarajući ispis, prikazani su ispod. ```python= veci_broj(5, 7) ``` <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Veći je drugi broj </pre> Naravno, funkcija može biti pozvana više puta, te se pozivu mogu proslijediti različite vrijednosti, kao u primjeru ispod. ```python= for i in range(4): print(f"Poziv {i + 1}:") broj1 = int(input("Unesite prvi broj: ")) broj2 = int(input("Unesite drugi broj: ")) veci_broj(broj1, broj2) print() ``` <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Poziv 1: Unesite prvi broj: 7 Unesite drugi broj: 10 Veći je drugi broj Poziv 2: Unesite prvi broj: 15 Unesite drugi broj: 8 Veći je prvi broj Poziv 3: Unesite prvi broj: 5 Unesite drugi broj: 5 Jednaki su Poziv 4: Unesite prvi broj: -2 Unesite drugi broj: 3 Veći je drugi broj </pre> ### Funkcije sa parametrima i sa povratnim vrijednostima Najčešći oblik funkcija su one koje primaju ulazne vrijednosti, obrađuju ih i vraćaju rezultat koji se može dalje koristiti u programu. Ovakve funkcije čine osnovu mnogih programskih rješenja, jer omogućavaju modularnost, ponovnu upotrebu i fleksibilnost. Kada funkcija vraća vrijednost, rezultat njenog rada postaje dostupan za daljnju upotrebu – bilo da se koristi za izračune, ispis, prosljeđivanje drugim funkcijama ili donošenje odluka u programu. :::success Funkcije koje vraćaju vrijednost koriste ključnu riječ `return`, koja ima nekoliko uloga. Prvo, `return` označava kraj izvršavanja funkcije; sve naredbe koje se nalaze nakon nje u tijelu funkcije neće biti izvršene. Drugo, `return` prosljeđuje rezultat obrade nazad programu koji je pozvao funkciju. Ovo omogućava da se funkcije integrišu u složenije operacije, jer njihovi rezultati mogu biti dinamički iskorišteni u različitim dijelovima programa. ::: Ova fleksibilnost čini funkcije koje vraćaju vrijednost najčešće korištenim tipom u programiranju. Na primjer, konverzija valuta ili izvođenje matematičkih operacija tipični su slučajevi gdje se funkcije koriste za obradu ulaznih vrijednosti i vraćanje rezultata za daljnje korištenje. **Primjer: Konverzija valuta** Sljedeća funkcija prihvata iznos novca u konvertibilnim markama (BAM), obrađuje ga tako da izračuna vrijednost u eurima (EUR), i vraća rezultat. ```python= def konvertuj_bam_u_eur(iznos_bam): iznos_eur = iznos_bam / 1.95583 return iznos_eur ``` Ovdje funkcija koristi ulazni parametar `iznos_bam` za izračunavanje iznosa u eurima, a ključna riječ `return` vraća rezultat obrade. Ova povratna vrijednost može se dalje koristiti u programu. Na primjer: ```python= eur = konvertuj_bam_u_eur(100) print(f"Iznos u eurima: {eur:.2f}") ``` Rezultat navedene funkcije naveden je ispod. <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Iznos u eurima: 51.13 </pre> :::warning ### Rezultat postoji, ali gdje je? Pozivanje funkcije koja vraća vrijednost, bez njenog daljnjeg korištenja, može dovesti do situacije gdje rezultat funkcije ostane nevidljiv. Ukoliko se povratna vrijednost funkcije ne pohrani u varijablu ili proslijedi naredbi `print()`, rezultat funkcije neće biti iskorišten, iako je funkcija uspješno izvršena. Ovo je slično kao da napišemo broj, npr. `5`, na liniji koda, bez ikakve radnje povezane s njim – broj postoji, ali se ništa s njim ne događa. ```python= konvertuj_bam_u_eur(100) ``` Ova funkcija zaista uradi konverziju novca, vrati vrijednost u eurima, ali rezultat nije prikazan, jer se s njim ništa ne radi. Ovo je slično pisanju: ```python= 5 ``` Broj `5` se nalazi u programu, ali ne postoji nikakva radnja povezana s njim. ::: ### Funkcije bez parametara i sa povratnim vrijednostima Funkcije bez parametara i sa povratnim vrijednostima često se koriste kada funkcija obavlja zadatak koji nije vezan za vanjske ulazne podatke, ali njen rezultat treba dalje koristiti. Ove funkcije izvode logiku unutar svog tijela i vraćaju rezultat pomoću ključne riječi `return`, čineći ga dostupnim ostatku programa. Ovakve funkcije su korisne kada je rezultat univerzalan ili ne zavisi od specifičnih ulaznih vrijednosti. Na primjer, funkcija koja vraća trenutnu verziju programa ili standardnu vrijednost kao što je broj π. Sljedeća funkcija ne prima nikakve ulazne vrijednosti, ali vraća vrijednost broja π. ```python= def daj_vrijednost_pi(): return 3.14159 ``` Ova funkcija uvijek vraća istu vrijednost – aproksimaciju broja π. Povratna vrijednost može se koristiti za izračune u matematičkim operacijama. ```python= pi = daj_vrijednost_pi() print(f"Vrijednost broja pi je: {pi}") ``` <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Vrijednost broja pi je: 3.14159 </pre> Funkciju možemo koristiti i u složenijim primjerima, poput primjera gdje od korisnika tražimo unos poluprečnika kruga, te računamo obim i površinu. ```python= print("Izračunavanje obima i površine kruga") poluprecnik = float(input("Unesite poluprečnik kruga: ")) pi = daj_vrijednost_pi() obim = 2 * pi * poluprecnik povrsina = pi * poluprecnik**2 print(f"Za krug s poluprečnikom {poluprecnik}:") print(f"Obim je: {obim:.2f}") print(f"Površina je: {povrsina:.2f}") ``` ## Funkcije koje pozivaju funkcije U mnogim situacijama, funkcije mogu koristiti druge funkcije kako bi organizovale kod i povećale čitljivost. Ovo je posebno korisno kada se određeni dijelovi logike mogu izdvojiti i ponovo koristiti. Na primjer, u primjeru iznad, kod izračunavanja obima i površine kruga, funkcije za ove izračune mogu se definisati odvojeno, dok glavna funkcija samo upravlja unosom i ispisom. Kada funkcije pozivaju druge funkcije, kod postaje modularniji i lakši za održavanje. Na primjer, umjesto da se svi proračuni piš u glavnom dijelu programa, organizovanija verzija koda je navedena ispod. ```python= def daj_vrijednost_pi(): return 3.14159 def obim_kruga(poluprecnik): pi = daj_vrijednost_pi() return 2 * pi * poluprecnik def povrsina_kruga(poluprecnik): pi = daj_vrijednost_pi() return pi * poluprecnik**2 poluprecnik = float(input("Unesite poluprečnik kruga: ")) obim = obim_kruga(poluprecnik) povrsina = povrsina_kruga(poluprecnik) print(f"Za krug s poluprečnikom {poluprecnik}:") print(f"Obim je: {obim:.2f}") print(f"Površina je: {povrsina:.2f}") ``` Funkcije `obim_kruga` i `povrsina_kruga` imaju jasno definisanu svrhu i koriste se za specifične zadatke unutar programa. Funkcija `obim_kruga` računa obim kruga na osnovu poluprečnika, dok funkcija `povrsina_kruga` računa površinu. Obje funkcije se oslanjaju na funkciju `daj_vrijednost_pi` kako bi dobile vrijednost broja π, što dodatno unapređuje modularnost i omogućava centralizovanu kontrolu nad ovom konstantom (npr. ako želimo promijeniti preciznost, možemo dodati ili ukloniti određen broj cifara iza decimalnog zareza). Prosljeđivanjem poluprečnika kao ulaznog parametra, ove funkcije mogu obavljati svoje zadatke bez potrebe za dupliranjem koda ili ručnim unosom formule u svakom dijelu programa. Kod koji koristi ove funkcije organizovan je tako da centralizuje unos podataka i ispis rezultata. Korisnik unosi vrijednost poluprečnika, a zatim se funkcije `obim_kruga` i `povrsina_kruga` pozivaju kako bi se izvršili izračuni. Nakon toga, rezultati se ispisuju na ekran. Ovakav način organizacije programa čini kod modularnijim i čitljivijim, jer svaki dio programa ima svoju jasno definisanu ulogu. Jedna od ključnih prednosti ovakvog pristupa je jasna podjela odgovornosti. Svaka funkcija obavlja samo jedan zadatak, što olakšava razumijevanje i održavanje koda. Modularni pristup omogućava jednostavnu izmjenu logike: ako se formula za obim ili površinu promijeni, izmjene se vrše samo u odgovarajućoj funkciji, a promjene se automatski odražavaju na sve dijelove programa koji tu funkciju koriste. Također, kod postaje čitljiviji i pregledniji, što je posebno važno kod složenijih programa. ### Redoslijed definisanja funkcija U Pythonu se redoslijed definisanja funkcija smatra ključnim za njihovu dostupnost i ispravno funkcionisanje. Kod se izvršava **liniju po liniju**, što znači da svaka funkcija mora biti **definisana prije nego što bude pozvana**. Ukoliko se funkcija pokuša pozvati prije nego što je Python registruje (tj. prije nego što se definicija funkcije pročita prilikom učitavanja koda), dolazi do greške: ```python= prikazi_poruku() # Greška - funkcija još nije definisana! def prikazi_poruku(): print("Zdravo!") ``` <pre style="background-color: #1e1e1e; color: red; padding: 20px;"> NameError: name 'prikazi_poruku' is not defined </pre> U ovom slučaju, greška nastaje jer Python pokušava izvršiti funkciju `prikazi_poruku()` prije nego što se njena definicija pročita. Tek kada interpreter dođe do dijela koda gdje se funkcija definiše, ona postaje dostupna za korištenje. Međutim, ako funkcija nije dostupna globalno, može biti **vidljiva drugim funkcijama** koje se nalaze **ispod nje** u kodu. Pythonom se definiše struktura funkcija prilikom učitavanja, dok se njihovo izvršavanje dešava tek kada budu pozvane. Na ovaj način omogućava se pozivanje funkcija koje su definisane niže, pod uslovom da poziv dolazi iz druge funkcije, a ne direktno iz glavnog dijela programa. ```python= def glavna_funkcija(): pomocna_funkcija() # Poziv funkcije definisane ispod def pomocna_funkcija(): print("Pozvana je pomoćna funkcija!") glavna_funkcija() ``` <pre style="background-color: #1e1e1e; color: white; padding: 20px;">Pozvana je pomoćna funkcija! </pre> Iako je `pomocna_funkcija` definisana **nakon** `glavna_funkcija`, greška se ne prijavljuje, jer se `glavna_funkcija` poziva tek nakon što Python pročita sve definicije funkcija. Ovaj princip omogućava izgradnju modularnih programa, u kojima funkcije međusobno pozivaju jedna drugu, bez obzira na redoslijed njihovog pojavljivanja u kodu. Ako se želi osigurati globalna dostupnost funkcija, preporučuje se da se **sve funkcije definišu na početku programa**, prije dijela u kojem se vrši glavno izvršavanje. Na taj način, potencijalne greške povezane s redoslijedom definisanja funkcija se izbjegavaju, a kod postaje pregledniji i konzistentniji. :::info ### Procedura ili funkcija? U programiranju, pojmovi "procedura" i "funkcija" često se koriste, ali imaju različita značenja: - **Funkcija** je blok koda koji izvršava određeni zadatak i obično **vraća vrijednost** pomoću ključne riječi `return`. - **Procedura** je blok koda koji izvršava određeni zadatak, ali **ne vraća vrijednost**. Procedura može, na primjer, samo ispisati podatke ili izvršiti određenu radnju. U Pythonu, svaka funkcija tehnički vraća vrijednost, čak i ako je to samo `None`. Stoga, razlika između funkcije i procedure leži u njenoj **namjeni**: - Ako funkcija **vraća vrijednost**, ona se smatra funkcijom. - Ako funkcija služi samo za obavljanje radnje, ali ništa ne vraća, ona se ponaša kao procedura. ::: ## Više o parametrima funkcija Kao što je već navedeno, parametri funkcija omogućavaju prosljeđivanje podataka funkcijama, čime se postiže fleksibilnost i ponovna upotreba koda. Dva osnovna tipa parametara u Pythonu su **obavezni** i **podrazumijevani (default)** parametri. Ovi parametri omogućavaju precizno prilagođavanje funkcija specifičnim potrebama, uz jednostavnu upotrebu u standardnim slučajevima. ### Obavezni parametri Obavezni parametri su parametri koji **moraju biti navedeni** prilikom poziva funkcije. Bez njih funkcija neće raditi i Python će prijaviti grešku. Ova vrsta parametara osigurava da funkcija dobije sve ključne podatke za izvršavanje zadatka. **Primjer: Pretvaranje valuta sa obaveznim parametrom** Za početak će biti korišten raniji primjer sa konverzijom novca iz BAM u EUR koristeći fiksni kurs: ```python= def konvertuj_u_eur(iznos_bam): kurs = 1.95583 # Fiksni kurs iznos_eur = iznos_bam / kurs return iznos_eur print(f"Iznos u eurima: {konvertuj_u_eur(100):.2f}") ``` <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Iznos u eurima: 51.13 </pre> Ukoliko bi funkcija bila pozvana bez vrijednosti 100, tj. ukoliko vrijednost `iznos_bam` nije naveden, Python prijavljuje grešku: ```python= konvertuj_u_eur() ``` <pre style="background-color: #1e1e1e; color: red; padding: 20px;"> TypeError: konvertuj_u_eur() missing 1 required positional argument: 'iznos_bam' </pre> Dakle, svi obavezni parametri moraju biti navedeni pri pozivu funkcije. ### Podrazumijevani (default) parametri Podrazumijevani parametri omogućavaju definisanje početne vrijednosti za parametar, koja će se koristiti ako taj parametar nije eksplicitno naveden prilikom poziva funkcije. Ovo je korisno za slučajeve gdje su određene vrijednosti česte ili standardne, ali ipak promjenjive. **Primjer: Pretvaranje valuta sa podrazumijevanim kursom** Dodavanjem parametra za kurs sa podrazumijevanom vrijednošću, dobija se kod u nastavku. ```python= def konvertuj_u_eur(iznos_bam, kurs=1.95583): iznos_eur = iznos_bam / kurs return iznos_eur ``` Sada funkcija može raditi i bez navođenja kursa, ali i sa navođenjem kursa: ```python= # Koristi podrazumijevani kurs print(f"Iznos u eurima 1: {konvertuj_u_eur(100):.2f}") # Koristi prilagođeni kurs print(f"Iznos u eurima 2: {konvertuj_u_eur(100, 2.0):.2f}") ``` <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Iznos u eurima 1: 51.13 Iznos u eurima 2: 50.00 </pre> ### Kombinovanje obaveznih i podrazumijevanih parametara :::warning Podrazumijevani parametri moraju uvijek dolaziti **nakon obaveznih parametara** u definiciji funkcije. Ovo omogućava funkcijama da koriste kombinaciju fiksnih i fleksibilnih vrijednosti. Dakle, nije moguće definisati podrazumijevane parametre, te nakon njih dodavati obavezne. ::: **Primjer: Pretvaranje valuta sa dodatnim opcijama** Dodavanjem parametra za proviziju koja se izražava u procentima, kao i naziv ciljne valute, funkcija dobija niz novih mogućnoti. ```python= def konvertuj_u_valutu(iznos, kurs=1.95583, provizija=0, valuta="EUR"): iznos_sa_provizijom = iznos - (iznos * provizija / 100) iznos_konvertovan = iznos_sa_provizijom / kurs return f"{iznos_konvertovan:.2f} {valuta}" ``` Funkcija `konvertuj_u_valutu` omogućava konverziju novca uz fleksibilnost korištenja različitih vrijednosti kursa, provizije i naziva valute. Prilikom poziva funkcije, moguće je koristiti podrazumijevane vrijednosti ili ih prilagoditi specifičnim potrebama. Ako se funkcija pozove bez navođenja dodatnih argumenata, koristiće podrazumijevani kurs, bez primjene provizije i podrazumijevanu valutu. Na primjer: ```python= print(konvertuj_u_valutu(100)) ``` Rezultat će biti: <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> 51.13 EUR </pre> Međutim, ako je potrebno koristiti prilagođeni kurs, moguće je proslijediti novu vrijednost kursa kao drugi argument: ```python= print(konvertuj_u_valutu(100, 2.0)) ``` Drugi način za pozivanje istog koda bi bio: ```python= print(konvertuj_u_valutu(100, kurs=2.0)) ``` U primjeru iznad, parametrima se pristupa korištenjem naziva parametra, kako bi se izbjegla dvosmislenost i zabuna. U oba slučaja, funkcija koristi kurs 2.0, a rezultat je: <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> 50.00 EUR </pre> Funkcija takođe omogućava primjenu provizije. Na primjer, ako se koristi kurs 2.0 i primjenjuje provizija od 5%, funkcija se može pozvati na jedan od načina ispod: ```python= print(konvertuj_u_valutu(100, 2.0, 5)) print(konvertuj_u_valutu(100, 2.0, provizija=5)) print(konvertuj_u_valutu(100, kurs=2.0, provizija=5)) ``` Rezultat je: <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> 47.50 EUR 47.50 EUR 47.50 EUR </pre> Konačno, moguće je navesti i naziv ciljne valute. Ako se, osim kursa i provizije, koristi valuta USD, funkcija se poziva ovako: ```python= print(konvertuj_u_valutu(100, kurs=2.0, provizija=5, valuta="USD")) ``` Rezultat je: <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> 47.50 USD </pre> Ovi primjeri pokazuju kako podrazumijevani i obavezni parametri mogu biti kombinovani za prilagođavanje funkcije različitim scenarijima, čineći kod čitljivim i fleksibilnim. :::info ### Parametar ili argument? Iako se pojmovi "parametar" i "argument" često koriste naizmjenično, oni imaju specifična značenja u programiranju i radu sa funkcijama. **Parametri** su varijable koje se navode u definiciji funkcije. Oni su mjesto gdje funkcija "očekuje" ulazne podatke. Na primjer, u funkciji ispod,`iznos` i `kurs` su **parametri**. ```python= def konvertuj_u_valutu(iznos, kurs=1.95583): return iznos / kurs ``` **Argumenti** su stvarne vrijednosti koje se prosljeđuju funkciji prilikom njenog poziva. Kada se funkcija pozove u kodu ispod, `100` i `2.0` su **argumenti** koji se prosljeđuju parametrima `iznos` i `kurs`. ```python= konvertuj_u_valutu(100, 2.0) ``` Razumijevanje razlike je važno jer parametri predstavljaju *definiciju onoga što funkcija treba*, dok argumenti daju *stvarne podatke* koje funkcija koristi. ::: ## Opseg parametara U Pythonu, **opseg** (eng. *scope*) parametara i varijabli određuje gdje su dostupni za korištenje unutar programa. Ovo je ključno za razumijevanje kako funkcije rade sa podacima i kako se podaci prenose unutar programa. U kontekstu funkcija, razlikuju se dvije osnovne vrste varijabli: - **Lokalne varijable** – Varijable koje su definisane unutar funkcije i dostupne su samo toj funkciji. - **Globalne varijable** – Varijable koje su definisane izvan svih funkcija i dostupne su cijelom programu. ### Lokalni opseg Kada se unutar funkcije definišu varijable, njihov opseg je ograničen na tu funkciju. Takve varijable nazivaju se **lokalne varijable** i njima se ne može pristupiti izvan funkcije. ```python= def kvadrat_broja(broj): kvadrat = broj ** 2 # Lokalna varijabla return kvadrat # Poziv funkcije print(f"Kvadrat broja 5 je: {kvadrat_broja(5)}") ``` U primjeru iznad, varijabla `kvadrat` je lokalna za funkciju `kvadrat_broja`. Ona postoji samo dok se funkcija izvršava i nije dostupna izvan nje. Ako pokušamo pristupiti ovoj varijabli izvan funkcije, Python će prijaviti grešku jer `kvadrat` ne postoji u globalnom opsegu. ```python= print(kvadrat) ``` <pre style="background-color: #1e1e1e; color: red; padding: 20px;"> NameError: name 'kvadrat' is not defined </pre> ### Globalni opseg Nasuprot tome, varijable koje su definisane izvan funkcija dostupne su svuda u programu. Takve varijable nazivaju se **globalne varijable**. **Primjer globalne varijable:** ```python= konstanta = 10 # Globalna varijabla def mnozi_konstantom(broj): rezultat = broj * konstanta # Korištenje globalne varijable return rezultat # Poziv funkcije print(f"Rezultat množenja sa konstantom: {mnozi_konstantom(5)}") ``` U ovom slučaju, `konstanta` je globalna varijabla koja je dostupna i unutar funkcije `mnozi_konstantom`. ### Konflikt između lokalnih i globalnih varijabli Ako unutar funkcije definišemo lokalnu varijablu istog naziva kao globalna varijabla, Python koristi lokalnu varijablu i ignoriše globalnu. Ovo može dovesti do zabune ako se ne pazi na imenovanje varijabli. ```python= konstanta = 10 # Globalna varijabla def mnozi_konstantom(broj): konstanta = 5 # Lokalna varijabla s istim imenom rezultat = broj * konstanta return rezultat # Poziv funkcije print(f"Rezultat množenja sa lokalnom konstantom: {mnozi_konstantom(5)}") print(f"Globalna konstanta: {konstanta}") ``` Rezultat je: <pre style="background-color: #1e1e1e; color: white; padding: 20px;"> Rezultat množenja sa lokalnom konstantom: 25 Globalna konstanta: 10 </pre> Iako globalna varijabla `konstanta` ima vrijednost 10, funkcija koristi svoju lokalnu varijablu `konstanta` sa vrijednošću 5. :::info ### Preporuke za rad sa opsegom varijabli 1. **Koristiti lokalne varijable gdje god je to moguće** – Lokalni opseg smanjuje rizik od konflikta imena i omogućava bolju kontrolu nad podacima unutar funkcije. 2. **Izbjegavati mijenjanje globalnih varijabli unutar funkcija** – Ako funkcija treba pristupiti globalnoj varijabli, koristiti je samo za čitanje. Za promjenu globalnih varijabli, neophodno je koristiti ključnu riječ `global`. ::: ### Napomena o parametarima funkcija Parametri funkcija su, po svojoj prirodi, lokalne varijable unutar funkcije. Oni postoje samo dok se funkcija izvršava i ne mogu biti pristupni izvan nje. ```python= def saberi(broj1, broj2): rezultat = broj1 + broj2 # 'rezultat' je lokalna varijabla return rezultat # Poziv funkcije print(f"Rezultat sabiranja: {saberi(3, 4)}") # Pokušaj pristupa varijabli 'rezultat' print(rezultat) ``` <pre style="background-color: #1e1e1e; color: red; padding: 20px;"> NameError: name 'rezultat' is not defined </pre> Ovo jasno pokazuje da su varijable definisane unutar funkcija dostupne samo u njihovom opsegu. Razumijevanje opsega parametara i varijabli ključno je za efikasno programiranje. Korištenjem lokalnih varijabli u funkcijama postiže se bolja modularnost i smanjuje rizik od grešaka povezanih s promjenom podataka. Globalne varijable treba koristiti s oprezom i samo kada su zaista potrebne. ## Sažetak Funkcije su ključni element svakog programskog jezika i jedan od najvažnijih načina za organizaciju koda. One omogućavaju primjenu DRY principa (*Don't Repeat Yourself*), čime se eliminiše višestruko pisanje iste logike i postiže veća efikasnost u razvoju softvera. Umjesto da se slične sekvence koda pišu više puta na različitim mjestima, funkcije omogućavaju njihovo definisanje na jednom mjestu i korištenje po potrebi. Na taj način programi postaju kraći, pregledniji i lakši za održavanje. Definicija funkcija zasniva se na parametrima i `return` naredbi, što omogućava kontrolu toka podataka unutar programa. Parametri funkcijama daju ulazne vrijednosti koje mogu varirati pri svakom pozivu, dok `return` omogućava vraćanje rezultata i njihovo daljnje korištenje. Ova kombinacija čini funkcije fleksibilnim i prilagodljivim različitim vrstama zadataka. Parametri mogu biti obavezni ili opcionalni, što dodatno povećava upotrebljivost funkcija i omogućava jednostavno rukovanje različitim scenarijima i vrstama podataka. Jedna od ključnih prednosti funkcija je modularnost – složeni zadaci razbijaju se na manje dijelove, što olakšava testiranje i pronalaženje grešaka. Programi postaju lakši za proširenje jer se nove funkcionalnosti dodaju kroz definisanje novih funkcija, bez potrebe za izmjenama postojećeg koda. Funkcije omogućavaju razvoj velikih projekata u timskom okruženju, gdje se različiti dijelovi aplikacije grade nezavisno, a zatim integrišu u cjelinu. Osim što omogućavaju ponovnu upotrebu koda, funkcije povećavaju čitljivost programa i olakšavaju praćenje toka izvršavanja. Jasno definisani ulazi i izlazi omogućavaju programerima da znaju šta se od funkcije očekuje bez potrebe za poznavanjem njene unutrašnje implementacije. Na ovaj način funkcije uvode nivo apstrakcije koji pojednostavljuje rad na kompleksnim aplikacijama i omogućava fokusiranje na logiku programa umjesto na detalje implementacije. U konačnici, funkcije su temelj svakog programskog jezika i osnovni alat u rukama programera. Bez njih, razvoj softverskih rješenja bio bi dugotrajan, sklon greškama i teži za održavanje. Funkcije ne samo da pojednostavljuju pisanje koda, već doprinose razvoju skalabilnih, efikasnih i profesionalnih aplikacija, bez obzira na njihovu veličinu ili složenost.