---
# System prepended metadata

title: Strukture odlučivanja
tags: [Programiranje koristeći Python]

---

Strukture odlučivanja
=====================

U prethodnom poglavlju obrađeni su osnovni mehanizmi za unos, obradu i ispis podataka. Iako ključni, ovi procesi ne omogućavaju programima da analiziraju situacije u kojima se nalaze. Drugim riječima, svi dosadašnji primjeri izvršavali su se na isti način, bez obzira na vrijednosti podataka ili okolnosti tokom izvršavanja.

Jedan od glavnih aspekata savremenih računarskih sistema je njihova sposobnost donošenja odluka i prilagođavanja različitim situacijama. Zahvaljujući strukturama odlučivanja, programi mogu provjeriti određeni uslov, analizirati dostupne podatke i, na osnovu rezultata, izabrati odgovarajući tok radnji. Na taj način, jednostavan, linearan tok koda (koji se izvršava od početka ka kraju) postaje fleksibilan mehanizam. Ovo omogućava programu da, zavisno od uslova, izvrši različite blokove koda i prilagodi se specifičnim okolnostima.

Proces donošenja odluka i upravljanja tokom programa temelji se na konceptu tačnosti i netačnosti izraza, koji se u Pythonu predstavlja logičkim vrijednostima `True` i `False`. Sposobnost programa da procijeni uslove i odredi koji dio koda treba izvršiti zasniva se upravo na ovoj osnovi.

Prije nego što se razmotre konkretne strukture odlučivanja, neophodno je upoznati se s osnovama logike u računarskom kontekstu. Uvodni korak ka ovom cilju je razumijevanje `bool` tipa podatka i osnovnih logičkih operacija, koji čine temelj donošenja odluka u programiranju.

## Tip `bool`

Tip podataka `bool` koristi se za predstavljanje logičkih vrijednosti, koje mogu biti samo dvije: `True` (tačno) i `False` (netačno). Ove vrijednosti su ključne za obradu logičkih izraza, gdje su operandi takođe `True` ili `False`, a rezultat operacije uvijek ostaje u okviru ove dvije vrijednosti. Iako na prvi pogled djeluju jednostavno, logičke vrijednosti i operacije tipa bool predstavljaju temelj računarstva i osnovu za donošenje odluka u programima.

Za rad sa logičkim vrijednostima koriste se logički operatori: `and`, `or` i `not`, čije definicije su:
* `a and b`: Vraća `True` samo ako su oba operanda `True`. U svim ostalim slučajevima rezultat je `False`.
* `a or b`: Vraća `True` ako je barem jedan operand `True`. Rezultat je `False` samo kada su oba operanda `False`.
* `not a`: Inverzuje vrijednost operanda, pa je rezultat `True` ako je operand `False`, i obratno.

:::info
### Tabele istinitosti (eng. truth tables)

Logički operatori `and`, `or`, i `not` slijede precizna pravila za određivanje rezultata na osnovu ulaznih vrijednosti. Sljedeće tabele prikazuju njihovo ponašanje u svim mogućim situacijama:

---

#### Tabela istinitosti za operator **`and`**
| **`a`**   | **`b`**   | **`a and b`** |
|---------|---------|-------------|
| `True`  | `True`  | `True`      |
| `True`  | `False` | `False`     |
| `False` | `True`  | `False`     |
| `False` | `False` | `False`     |
---
#### Tabela istinitosti za operator **`or`**
| **`a`**   | **`b`**   | **`a or b`** |
|---------|---------|------------|
| `True`  | `True`  | `True`     |
| `True`  | `False` | `True`     |
| `False` | `True`  | `True`     |
| `False` | `False` | `False`    |
---
#### Tabela istinitosti za operator **`not`**
| **`a`**   | **`not a`** |
|---------|-----------|
| `True`  | `False`   |
| `False` | `True`    |
:::

Redoslijed izvršavanja operatora (prioritet) igra ključnu ulogu u određivanju rezultata ovakvih izraza. Operator `not` ima najviši prioritet i izvodi se prvi, slijedi ga `and`, dok `or` ima najniži prioritet. Zagrade se koriste za kontrolisanje ovog redoslijeda kako bi izrazi bili jasniji i lakše razumljivi.

:::info
Ponekad, ista logička funkcija može biti izražena na različite načine. Izrazi `(a and b)` i `not (not a or not b)` su logički ekvivalentni. Da bi se dokazala ova ekvivalentnost, može se koristiti tabela istinitosti koja ispituje sve moguće kombinacije vrijednosti operanada. Ovakav pristup omogućava jasnu verifikaciju tačnosti složenijih logičkih izraza.

| `a`     | `b`     | `a and b` | `not a` | `not b` | `not a or not b` | `not (not a or not b)` |
|:-------:|:-------:|:---------:|:-------:|:-------:|:----------------:|:----------------------:|
| `False` | `False` | `False`   | `True`  | `True`  | `True`           | `False`                |
| `False` | `True`  | `False`   | `True`  | `False` | `True`           | `False`                |
| `True`  | `False` | `False`   | `False` | `True`  | `True`           | `False`                |
| `True`  | `True`  | `True`    | `False` | `False` | `False`          | `True`                 |

:::

Proučavanje načina na koji se manipuliše logičkim izrazima naziva se Booleova logika. Ova oblast matematike je veoma važna u računarstvu, jer čini osnovu za rad računarskog hardvera i teorijske osnove računanja.

U programiranju, logički izrazi tipa bool koriste se za donošenje odluka. Kada program treba da odluči šta će raditi dalje, koristi logičke izraze za provjeru određenih uslova. Na primjer, provjera ispravnosti lozinke zasniva se na logičkom izrazu: ako je lozinka ispravna (`True`), program dozvoljava pristup. Ako lozinka nije ispravna (`False`), pristup se odbija i traži se ponovno unošenje.

Ovaj način rada omogućava programima da odgovore na različite situacije i prilagode svoje ponašanje na osnovu podataka koje obrade. Logički izrazi su zato osnovni alat za izgradnju programa koji se dinamički prilagođavaju korisničkim potrebama.

Logički tip bool zasniva se na vrijednostima `True` i `False`, koje se koriste za osnovne logičke operacije. Sljedeća tabela prikazuje njihove vrijednosti, doslovne vrijednosti, operacije i operatore:

<table>
  <tr>
    <td><strong>Vrijednosti</strong></td>
    <td style="text-align: center;">tačno, netačno</td>
  </tr>
  <tr>
    <td><strong>Doslovne vrijednosti</strong></td>
    <td style="text-align: center;"><code>True</code>, <code>False</code></td>
  </tr>
  <tr>
    <td><strong>Operacije</strong></td>
    <td style="text-align: center;">i, ili, ne</td>
  </tr>
  <tr>
    <td><strong>Operatori</strong></td>
      <td style="text-align: center;"><code>and</code>, <code>or</code>, <code>not</code></td>
  </tr>
</table>

### Operacije poređenje

Operatori poređenja u Pythonu omogućavaju procjenu odnosa između dvije vrijednosti. Za razliku od matematičkih operacija, gdje rezultat obično ima isti tip kao i operandi, **rezultat poređenja je uvijek logička vrijednost (`True` ili `False`)**. Operandi mogu biti različitih tipova, poput cijelih brojeva, realnih brojeva ili stringova, ali rezultat poređenja je uvijek tipa `bool`.

| **Operator** | **Značenje**              | **`True` primjer** | **`False` primjer** |
|--------------|---------------------------|--------------------|---------------------|
| `==`         | jednako                   | `5 == 5`           | `5 == 8`            |
| `!=`         | nije jednako              | `7 != 3`           | `7 != 7`            |
| `<`          | manje od                  | `4 < 9`            | `6 < 6`             |
| `<=`         | manje ili jednako         | `8 <= 8`           | `10 <= 7`           |
| `>`          | veće od                   | `15 > 6`           | `5 > 15`            |
| `>=`         | veće ili jednako          | `12 >= 7`          | `6 >= 12`           |

Operatori poređenja imaju **niži prioritet** od aritmetičkih operatora, ali **viši prioritet** od logičkih operatora. Ovo znači da se izrazi prvo procjenjuju na osnovu aritmetičkih operacija, zatim dolazi na red poređenje, a na kraju se obrađuju logički operatori.

U narednom primjeru prvo se izvršava aritmetička operacija `10 - 3`, koja daje rezultat `7`. Zatim slijedi poređenje `8 > 6`, koje vraća `True`, i poređenje `7 < 5`, koje vraća `False`. Na kraju, logički operator `and` kombinuje rezultate poređenja. Budući da jedan od rezultata poređenja `(7 < 5)` iznosi `False`, konačni rezultat cijelog izraza je `False`.

```python=
rezultat = 8 > 6 and 10 - 3 < 5
print(rezultat)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
False
</pre>

#### *Truthy* i *falsy* vrijednosti za `int`, `float` i `str`

U Pythonu, tipovi podataka `int`, `float` i `str` imaju specifična pravila koja određuju da li se njihova vrijednost procjenjuje kao *truthy* (tačna) ili *falsy* (netačna)[^truthy]. Ovo pravilo omogućava korištenje ovih vrijednosti u logičkim izrazima bez potrebe za dodatnim provjerama.

[^truthy]: Pojmovi *truthy* i *falsy* označavaju vrijednosti koje se u logičkom kontekstu procjenjuju kao `True` ili `False`. Mogući prevod na bosanski jezik bio bi "tačne vrijednosti" za *truthy* i "netačne vrijednosti" za *falsy*, ali u ovom tesktu zadržani su originalni termini zbog njihove standardne upotrebe u IT zajednici i lakšeg povezivanja s tehničkom dokumentacijom.

Za tip `int`:
* *Falsy*: Vrijednost `0` se smatra netačnom (`False`).
* *Truthy*: Sve druge vrijednosti (pozitivne i negativne) smatraju se tačnima (`True`).

```python=
print(bool(0))
print(bool(42))
print(bool(-42))
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
False
True
True
</pre>

Za tip `float`:
* *Falsy*: Vrijednost `0.0` se smatra netačnom (`False`).
* *Truthy*: Sve druge vrijednosti (pozitivne i negativne) smatraju se tačnima (`True`).

```python=
print(bool(0.0))
print(bool(3.14))
print(bool(-2.7))

```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
False
True
True
</pre>

Za tip `str`:
* *Falsy*: Prazan string `""` se smatra netačnom (`False`) vrijednošću.
* *Truthy*: Svi neprazni stringovi smatraju se smatraju se tačnima (`True`), uključujući stringove koji sadrže samo razmake ili specijalne znakove.

```python=
print(bool(""))
print(bool("tekst"))
print(bool(" "))
print(bool("!"))
print(bool("True"))
print(bool("False"))
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
False
True
True
True
True
True
</pre>

> [!Warning] Pažnja
> U Pythonu, string `"False"` tumači se kao `True` kada se procjenjuje njegova istinitost u logičkom izrazu. Ovo može biti neočekivano, međutim, Python procjenjuje svaki neprazan string kao *truthy*, bez obzira na njegov sadržaj.

#### Poređenje `float` brojeva

Poređenje brojeva tipa `float` može biti nepouzdano zbog problema s preciznošću pri njihovom predstavljanju u memoriji računara. Zbog zaokruživanja, rezultat poređenja dva broja koji su matematički jednaki može biti neočekivan. Na primjer:

```python=
print(0.1 + 0.2 == 0.3)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
False
</pre>

U Pythonu `0.1 + 0.2` nije tačno jednak `0.3` zbog zaokruživanja pri predstavljanju brojeva u binarnom formatu.

Da bi se izbjegli problemi s preciznošću, koristi se metoda poređenja sa tolerancijom (epsilon). Dva broja se smatraju približno jednakim ako je njihova razlika manja od unaprijed definisane tolerancije. Ovo se može provjeriti pomoću matematičkog izraza:

```python=
epsilon = 0.000000001
razlika = 0.1 + 0.2 - 0.3
rezultat = razlika > -epsilon and razlika < epsilon
print(rezultat)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
True
</pre>


#### Leksičko poređenje stringova

Pored numeričkih vrijednosti, operatori za poređenje mogu se koristiti i za stringove. Stringovi se porede prema leksičkom redoslijedu (redoslijedu znakova u Unicode tabeli). Na primjer:
* `'a' < 'b'` vraća `True` jer slovo `a` dolazi prije `b`.
* `'jabuka' > 'jab'` vraća `True` jer je string `'jabuka'` duži i dolazi nakon `'jab'`.

Nakon upoznavanja s osnovnim vrijednostima i operatorima tipa bool, moguće je koristiti ove elemente za rješavanje konkretnih problema. Jedan od klasičnih primjera primjene logičkih operatora je provjera da li je određena godina prestupna. 

| **Izraz**        | **Vrijednost** | **Objašnjenje**                                            |
|------------------|----------------|------------------------------------------------------------|
| `'abc' < 'abd'`  | `True`         | Slovo `c` ima manju vrijednost od slova `d`.               |
| `'abc' < 'aBd'`  | `False`        | Unicode vrijednost malih slova je veća od velikih.         |
| `'abc' < 'ABC'`  | `False`        | Unicode vrijednost malih slova je veća od velikih.         |
| `'abc' < 'abcd'` | `True`         | Kod podudaranja kraći string dolazi prije dužeg.           |
| `'abd' < 'abcd'` | `False`        | Slovo `d` ima veću vrijednost od slova `c`.                |


Godina se smatra prestupnom ako zadovoljava sljedeće uslove: mora biti djeljiva sa 4, ali ako je istovremeno djeljiva sa 100, onda nije prestupna osim ako je djeljiva i sa 400. Ova pravila osiguravaju pravilnu usklađenost kalendara s dužinom astronomskih godina.

```python=
godina = 1189
prestupna = (godina % 4 == 0) and (godina % 100 != 0 or godina % 400 == 0)
print("Godina", godina, "je prestupna:", prestupna)
```

<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Godina 1189 je prestupna: False
</pre>

### Skraćeno izračunavanje

Skraćeno izračunavanje (eng. Short-Circuit Evaluation) je tehnika optimizacije u kojoj Python preskače daljnju procjenu izraza čim postane jasno kakav će biti konačni rezultat. Ovo se primjenjuje na logičke operatore `and` i `or`, jer oni mogu odlučiti o ishodu izraza bez potrebe za procjenom svih operanada.

Kod operatora `and`, Python prestaje s procjenom čim naiđe na `False`, jer rezultat izraza ne može biti `True`. Na primjer:

```python=
x = 5
rezultat = (x > 10) and (x / 0 == 1)
print(rezultat)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
False
</pre>

U ovom slučaju, prvi operand `(x > 10)` vraća `False`, pa Python ne procjenjuje drugi operand `(x / 0 == 1)`. Ovo sprječava grešku dijeljenja s nulom.

Kod operatora or, Python prestaje s procjenom čim naiđe na `True`, jer rezultat izraza ne može biti `False`. Na primjer:

```python=
x = 5
rezultat = (x < 10) or (x / 0 == 1)
print(rezultat)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
True
</pre>

### Vraćanje vrijednosti u logičkim izrazima

Logički operatori `and` i `or` u Pythonu ne vraćaju uvijek logičke vrijednosti `True` ili `False`. Umjesto toga, rezultat ovih operatora može biti vrijednost jednog od operanada, a ta vrijednost ne mora biti tipa `bool`. Ovo ponašanje je omogućeno zahvaljujući *skraćenom izračunavanju* i procjene *truthy* i *falsy* vrijednosti operanada.

Kod operatora `and`:
* Ako je **prvi operand *falsy***, odmah se vraća njegova vrijednost, a drugi operand se ne procjenjuje.
* Ako je **prvi operand *truthy***, rezultat zavisi od drugog operanda i njegova vrijednost se vraća.

Kod operatora `or`:
* Ako je **prvi operand *truthy***, odmah se vraća njegova vrijednost, a drugi operand se ne procjenjuje.
* Ako je **prvi operand *falsy***, rezultat zavisi od drugog operanda i njegova vrijednost se vraća.

Logički operatori `and` i `or` omogućavaju donošenje odluka na osnovu toga da li su vrijednosti operanada "truthy" ili "falsy". Kada se koriste, oni procjenjuju vrijednosti operanada i vraćaju rezultat u zavisnosti od njihovog ponašanja, što omovućava biranje između postojećih vrijednosti ili korištenja zamjenske vrijednosti. Ovo ponašanje čini ih posebno korisnim u situacijama kada je potrebno utvrditi da li se određena vrijednost može koristiti ili je potrebno upotrijebiti alternativnu.

```python=
korisnicko_ime = "admin"
prikaz = korisnicko_ime or "gost"
print(prikaz)

korisnicko_ime = ""
prikaz = korisnicko_ime or "gost"
print(prikaz)

broj_poena = 0
ukupno = broj_poena and "Korisnik ima poene" or "Poeni nisu dodijeljeni"
print(ukupno)

broj_poena = 9
ukupno = broj_poena and "Korisnik ima poene" or "Poeni nisu dodijeljeni"
print(ukupno) 
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
admin
gost
Poeni nisu dodijeljeni
Korisnik ima poene
</pre>

Prvo se razmatra varijabla `korisnicko_ime`. U prvom izrazu, ona ima vrijednost `"admin"`, koja se smatra *truthy*, jer nije prazan string. Operator `or` prvo procjenjuje tu vrijednost i, budući da je *truthy*, odmah je vraća kao rezultat, zanemarujući drugi operand `"gost"`. Zbog toga se ispisuje `"admin"`.

U drugom izrazu, varijabla `korisnicko_ime` sadrži prazan string `""`, koji je *falsy*. Operator or u ovom slučaju procjenjuje prvi operand, ali kako je *falsy*, prelazi na drugi operand i vraća njegovu vrijednost `"gost"`. Rezultat ovog dijela koda je `"gost"`.

Dalje, kod koristi operator `and` zajedno s operatorom `or` za određivanje odgovarajuće poruke na osnovu vrijednosti varijable `broj_poena`. Kada je `broj_poena` `0`, što je *falsy* vrijednost, operator and odmah vraća tu vrijednost bez procjene drugog operanda. Rezultat and operacije je `0`, koji je također *falsy*, pa operator or prelazi na svoj drugi operand `"Poeni nisu dodijeljeni"` i vraća ga kao rezultat. Ispisana poruka je `"Poeni nisu dodijeljeni"`.

Na kraju, kada je `broj_poena` `9`, što je *truthy* vrijednost, operator `and` procjenjuje oba operanda i vraća vrijednost drugog operanda `"Korisnik ima poene"`. Zatim, operator `or` procjenjuje taj rezultat, koji je *truthy*, i vraća ga kao konačni rezultat. U ovom slučaju, ispisana poruka je `"Korisnik ima poene"`.

Ovaj kod demonstrira kako se operatori and i or mogu koristiti za dinamičko biranje između vrijednosti, omogućavajući prilagodljivo ponašanje programa na osnovu stanja varijabli.


## Uvod u strukture odlučivanja

Programi se sastoje od naredbi koje određuju kako će se izvršavati zadaci. Kontrola toka izvršavanja tih naredbi ostvaruje se pomoću **upravljačkih struktura**, koje definišu **redoslijed** i uslove pod kojima će se određene naredbe izvršavati.

Upravljačke strukture mogu se podijeliti u tri osnovne vrste:

1. **Sekvencijalne strukture**: Naredbe se izvršavaju redoslijedom kako su zapisane, jedna za drugom, bez mogućnosti prilagođavanja.
2. **Strukture odlučivanja**: Omogućavaju grananje toka programa na osnovu logičkih uslova.
3. **Strukture ponavljanja**: Omogućavaju ponavljanje određenih dijelova koda dok su ispunjeni određeni uslovi.

Dosadašnji primjeri ilustrovali su najjednostavniju vrstu upravljačke strukture — **sekvencijalnu strukturu**, u kojoj se naredbe izvršavaju redoslijedom kojim su zapisane. Ova struktura je dovoljna za osnovne zadatke, ali većina praktičnih problema zahtijeva veću fleksibilnost.

U praktičnim situacijama, program mora biti sposoban da:
* Procijeni trenutno stanje.
* Donese odluke o tome koji će dio koda izvršiti na osnovu rezultata logičkih izraza.

Za takve slučajeve koriste se **strukture odlučivanja**, koje omogućavaju selektivno izvršavanje određenih dijelova programa. Umjesto da sve naredbe budu izvršene redom, kao u sekvencijalnoj strukturi, pomoću struktura odlučivanja program može reagovati na različite okolnosti, prilagoditi se promjenama ulaznih podataka i efikasnije rješavati složenije probleme. Ove strukture predstavljaju osnovu za donošenje odluka u programiranju i omogućavaju programima da se ponašaju dinamički, u skladu s kontekstom i potrebama.

### Zašto su odluke potrebne?

Program koji uvijek obrađuje sve scenarije na isti način često nije praktičan. Na primjer, aplikacija za online trgovinu može zahtijevati obračunavanje popusta na osnovu vrijednosti narudžbe. Ako iznos kupovine premašuje određeni prag, potrebno je primijeniti popust i smanjiti cijenu. U suprotnom, naplaćuje se puna cijena. Bez mehanizma donošenja odluka, program ne bi mogao razlikovati ove situacije i uvijek bi obračunavao isti iznos, bez obzira na vrijednost narudžbe. 

Slično, prilikom unosa podataka od korisnika, program može provjeriti da li je unesena vrijednost ispravna (npr. pozitivan broj). A Ako jeste, nastavlja s proračunima, a ako nije, može obavijestiti korisnika o grešci i zatražiti novi unos. Odluke su, dakle, ključne za prilagođavanje programa različitim okolnostima i ulaznim vrijednostima.

### Primjeri iz svakodnevnog života

Ideja donošenja odluka u programima može se lako razumjeti ako se posmatraju svakodnevne situacije koje zahtijevaju logičku procjenu i izbor reakcije. Na primjer, prilikom vožnj automobilom mogu se napraviti naredn izbori:
* Ako je na semaforu zeleno svjetlo, nastavlja se vožnja.
* Ako je crveno svjetlo, zaustavlja se kretanje.
* Ako je žuto svjetlo, vozač procjenjuje da li može sigurno proći ili treba stati.

Ova jednostavna analiza semafora pokazuje kako se na osnovu jednog uslova (boje svjetla) donose različite odluke (kretanje, zaustavljanje, priprema). U programiranju, potpuno isti princip važi kada se analizira vrijednost varijable, rezultat poređenja ili logički izraz. 

Na primjer, ako je korisnikova lozinka ispravna (ekvivalent zelenom svjetlu), program ulazi u sistem, a ako nije (ekvivalent crvenom svjetlu), program traži ponovni unos lozinke. Ukoliko je situacija nejasna (ekvivalent žutom svjetlu), program može izvršiti dodatnu provjeru ili donijeti neku drugu odluku.

### Reagovanje na rezultate logičkih izraza

Osnova donošenja odluka u programima zasniva se na logičkoj procjeni. Tip podataka `bool` i logički izrazi predstavljaju temelj za uspostavljanje uslova. Program ne donosi odluke *nasumično*, već evaluira logički izraz i posmatra njegov rezultat, koji može biti `True` ili `False`.

Kada je izraz `True`, to znači da su određeni kriteriji ispunjeni. Program tada prelazi na skup instrukcija predviđen za slučaj ispunjenih uslova. U suprotnom, kada je izraz `False`, uslovi nisu ispunjeni, pa se izvršavaju alternativne instrukcije. To može uključivati ispis poruke o grešci, traženje novog unosa ili prekid rada programa.

Ova logička osnova omogućava programima donošenje odluka na osnovu konkretnih podataka i trenutnog stanja, umjesto praćenja unaprijed definisanog, statičnog toka. Umjesto da instrukcije uvijek slijede isti redoslijed, omogućava se grananje toka programa i izbor pravca izvršavanja u zavisnosti od rezultata logičkih provjera. Na taj način, kod se ne posmatra kao statičan niz naredbi, već kao dinamičan sistem mogućnosti.

Za implementaciju ove logike koriste se posebne programske konstrukcije čija uloga je omogućavanje grananja toka programa u skladu s rezultatima logičkih procjena. U narednim sekcijama bit će prikazan način korištenja ovih konstrukcija i objašnjeno kako one doprinose raznovrsnosti i fleksibilnosti u radu programa.

## Naredba `if`

Naredba `if` omogućava donošenje odluka tokom izvršavanja programa na osnovu rezultata logičkih izraza. Pomoću ove naredbe program može provjeriti da li je određeni uslov ispunjen i, u zavisnosti od toga, izvršiti pripadajuće naredbe. 

Osnovna sintaksa naredbe if sastoji se od ključne riječi `if`, iza koje slijedi logički izraz (boolov izraz), i bloka koda koji se izvršava ako je uslov tačan. Struktura izgleda ovako:

```python=
if uslov:
    # blok koda koji se izvršava ako je uslov tačan
```

Dijagram toka naredbe `if` može se prikazati na sljedeći način:

```mermaid
graph TD
    A([Početak if naredbe]) --> B{Provjera uslova}
    B -- True --> C[Blok koda za tačan uslov]
    C --> D([Kraj if naredbe])
    B -- False --> D
```

Dijagram pokazuje da program provjerava da li je uslov tačan (`True`). Ako jeste, blok koda se izvršava, nakon čega program prelazi na kraj. Ako uslov nije tačan, ne preduzima se nikakva akcija, a program nastavlja dalje.


:::success
### Blok koda

Blok koda predstavlja skup naredbi koje su sastavni dio određene komande, kao što je komanda `if`. Ove naredbe su odvojene od ostatka programa i prepoznaju se na osnovu uvlačenja redova, što vizuelno i tehnički označava njihovu pripadnost.

#### Primjer bloka koda u naredbi `if`

```python
if True:
    print("Ovaj kod se uvijek izvršava.")
    print("Zato što je uslov uvijek tačan.")

print("Ovaj ispis nije dio bloka koda.")
```

Dvije naredbe `print` koje slijede nakon naredbe `if` pripadaju njenom bloku koda. Budući da treća naredba `print` nije uvučena, ona nije dio bloka naredbe `if`. Zbog toga će se ona uvijek izvršavati, bez obzira na rezultat uslova.

#### Pravila za uvlačenje

Prilikom kreiranja blokova koda u Python-u, potrebno je slijediti naredna pravila:
* **Uvlačenje redova je obavezno**: U Pythonu, uvlačenje redova nije samo vizualni stil, već potreba. Sve naredbe koje pripadaju istom bloku moraju biti uvlačene istim brojem razmaka ili tabulatora.
* **Konzistentnost**: Unutar jednog programa preporučuje se korištenje samo razmaka ili samo tabulatora za uvlačenje. Python zajednica preporučuje upotrebu četiri razmaka.
* **Hijerarhija blokova**: Umetnuti blokovi [^umetnuti] koda zahtijevaju dodatno uvlačenje kako bi se jasno naznačila njihova pripadnost.
:::

[^umetnuti]: Kada se kreira novi blok koda unutar postojećeg bloka naziva se umetnutim ili ugniježdenim (eng. nested) blokom. Primjeri koji koriste umetnute blokove bit će razmatrati u nastavku.

Sljedeći primjer pokazuje osnovnu upotrebu naredbe `if`:

```python=
if True:
    print("Ovaj kod se uvijek izvršava.")
    print("Zato što je uslov uvijek tačan.")

print("Ovaj ispis nije dio bloka koda.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Ovaj kod se uvijek izvršava.
Zato što je uslov uvijek tačan.
Ovaj ispis nije dio bloka koda.
</pre>

U ovom primjeru, logički izraz `True` predstavlja najjednostavniji mogući uslov, koji je uvijek tačan. Blok koda unutar naredbe `if` uvijek će se izvršiti. Naredba `print` izvan bloka koda se izvršava neovisno o uslovu.

Međutim, ovako napisan kod nema praktičnu upotrebu jer uvijek daje isti rezultat. Korisnije je postaviti uslov koji zavisi od vrijednosti varijable koja se može mijenjati:

```python=
izvrši = False
if izvrši:
    print("Ovaj dio koda se izvršava samo ako je 'izvrši' tačan.")

print("Ovaj ispis je uvijek vidljiv.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Ovaj ispis je uvijek vidljiv.
</pre>

U ovom primjeru, logički izraz zavisi od vrijednosti varijable `izvrši`. Budući da je `False`, blok koda unutar naredbe `if` se neće izvršiti, čineći i ovaj primjer veoma sličnim prethodnom. Ipak, prava moć naredbe `if` postaje vidljiva kada vrijednost varijable u uslovu došla od korisnika ili neke prethodne obrade podataka:

```python=
izvrši = input("Da li želite izvršiti kod? (da/ne): ")
if izvrši == "da":
    print("Kod je izvršen.")

print("Izvršavanje završeno.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Da li želite izvršiti kod? (da/ne): da <kbd>⏎ Enter</kbd>
Kod je izvršen.
Izvršavanje završeno.
</pre>

<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Da li želite izvršiti kod? (da/ne): ne <kbd>⏎ Enter</kbd>
Izvršavanje završeno.
</pre>

U primjeru iznad korisnički unos se poredi sa stringom `"da"` kako bi se odredilo da li je uslov tačan. Ako korisnik unese `"da"`, blok koda unutar naredbe `if` će se izvršiti, a u suprotnom neće. Na ovaj način, program reaguje na ulazne podatke.

### Praktični primjeri

Iako se naredba `if` u prethodno navedenim primjerima koristi za ispis poruka, njena prava snaga leži u prilagođavanju toka programa. Na primjer, sljedeći kod izračunava apsolutnu vrijednost broja:

```python=
broj = int(input("Unesite broj: "))
if broj < 0:
    broj = -broj
    
print(f"Apsolutna vrijednost je {broj}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj: -42 <kbd>⏎ Enter</kbd>
Apsolutna vrijednost je 42
</pre>

Ako je uneseni broj negativan, njegova vrijednost se negira, a u suprotnom se uneseni broj ne mijenja. Na ovaj način se dobija apsolutna vrijednost broja.

Zanimljiv primjer je i upotreba `if` naredbe za uređivanje ispisa u zavisnosti od unesene vrijednosti.

```python=
broj = int(input('Unesite broj: '))

print('Uneseni broj ', end='')

if broj < 1:
    print('ni', end='')

print('je pozitivan')
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj: -10 <kbd>⏎ Enter</kbd>
Uneseni broj nije pozitivan
</pre>

<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj: 10 <kbd>⏎ Enter</kbd>
Uneseni broj je pozitivan
</pre>

U ovom primjeru, ako je uneseni broj manji od `1`, dodat će se riječ `"ni"` prije `"je pozitivan"`, čime se dobija ispis `"Uneseni broj nije pozitivan"`. U suprotnom, ispisaće se samo `"Uneseni broj je pozitivan"`. Ključni element ovog primjera je kombinacija naredbi `print` i parametra `end`, što omogućava fleksibilnost u sastavljanju izlaznog teksta bez prekida u linijama ispisa.

Naredni primjer demonstrira kako se naredba `if` može koristiti za sortiranje dvije unesene vrijednosti:

```python=
broj1 = int(input('Unesite prvi broj: '))
broj2 = int(input('Unesite drugi broj: '))

if broj1 > broj2:
    temp = broj1
    broj1 = broj2
    broj2 = temp

print('Sortirani:', broj1, broj2)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite prvi broj: 42 <kbd>⏎ Enter</kbd>
Unesite drugi broj: 41 <kbd>⏎ Enter</kbd>
Sortirani: 41 42
</pre>

Cilj je osigurati da manji broj bude dodijeljen varijabli `broj1`, a veći varijabli `broj2`. Prilikom izvršavanja, program prvo poredi vrijednosti varijabli `broj1` i `broj2`. Ako je `broj1` veći od `broj2`, tada se njihove vrijednosti zamjenjuju kako bi ispunile uslov sortiranja. Za ovu zamjenu koristi se pomoćna varijabla `temp`, koja privremeno čuva vrijednost jedne od varijabli.

:::info
Pomoćna varijabla `temp` ključna je za proces zamjene. Bez nje, direktno dodjeljivanje vrijednosti između varijabli bi rezultiralo gubitkom jedne od vrijednosti. Na primjer:

```python=
broj1 = broj2  # Ova linija bi prebrisala originalnu vrijednost 'broj1'
broj2 = broj1  # Ovdje bi obje varijable imale istu vrijednost
```
Korištenjem `temp`, proces izgleda ovako:
```python=
temp = broj1
broj1 = broj2
broj2 = temp
```
1. Vrijednost `broj1` se pohranjuje u `temp`.
2. Varijabla `broj1` se veže za varijablu `broj2`, čime se gubi veza varijable `broj1` sa prvobitnom vrijednšću, ali koja je trenutno vezana za `temp`.
3. Vrijednost vezana za `temp` (prvobitna vrijednost `broj1`) se veže za varijablu `broj2`.

Ova tehnika osigurava da se originalne vrijednosti obje varijable pravilno zamijene bez gubitka podataka. [^parovi]
:::

[^parovi]: U Pythonu postoji elegantniji način zamjene vrijednosti između dvije varijable pomoću sintakse `broj1, broj2 = broj2, broj1`. Ovaj pristup koristi nekoliko koncepata, kao što su kreiranje parova i raspakivanje, koji omogućavaju istovremeno dodjeljivanje novih vrijednosti varijablama bez potrebe za pomoćnom varijablom. Iako je ova metoda kraća i efikasnija, u ovom primjeru je izostavljena kako bi logika zamjene bila jasnija početnicima. Napredniji koncepti koji omogućavaju ovakvu sintaksu bit će objašnjeni u kasnijim poglavljima.



Naredba `if` predstavlja primjer **strukture odluke s jednom alternativom**. Ova struktura omogućava izvršavanje određenog dijela koda *samo ako je uslov tačan*, dok se **u suprotnom ne preduzima nikakva akcija**.

Naredba `if` je jednostavna, ali izuzetno korisna struktura za donošenje odluka u programima. Međutim, u situacijama kada je potrebno izvršiti jednu akciju ako je uslov tačan, *a drugu akciju ako nije*, naredba `if` sama po sebi nije dovoljna. U takvim slučajevima koristi se proširenje ove naredbe – naredba `if`-`else`.


## Naredba `if`-`else`

Naredba `if`-`else` pruža dodatnu fleksibilnost u odnosu na `if` naredbu, tako što omogućava izvršavanje alternativnog bloka koda kada uslov nije tačan. Ova struktura se koristi kada je potrebno donijeti odluku između dva moguća toka programa.

Naredba `if`-`else` uvodi koncept **strukture odluke s dvije alternative**, gdje jedna alternativa predstavlja ispunjenje uslova, a druga njegovo neispunjenje. Ovakav način odlučivanja omogućava programima da obrade sve moguće scenarije i odgovarajuće reaguju.

Sintaksa naredbe `if`-`else` u Python-u izgleda ovako:

```python
if uslov:
    # blok koda koji se izvršava ako je uslov tačan
else:
    # blok koda koji se izvršava ako je uslov netačan
```

Naredba if-else sastoji se od nekoliko ključnih elemenata koji omogućavaju njeno pravilno funkcionisanje:

1. **Ključna riječ `if`**: Označava početak provjere uslova.
2. **Logički izraz (uslov)**: Boolov izraz koji se evaluira kao `True` ili `False`.
3. **Blok koda nakon `if`**: Izvršava se samo ako je uslov tačan.
4. **Ključna riječ `else`**: Označava početak alternativnog bloka koda.
5. **Blok koda nakon `else`**: Izvršava se samo ako je uslov netačan.

I `if` i `else` blokovi moraju biti uvučeni istim brojem razmaka, u skladu s Python pravilima za uvlačenje redova. Svaka grana naredbe `if`-`else` može sadržavati jednu ili više naredbi.

Ova struktura se može vizualizirati pomoću dijagrama toka koji prikazuje tok izvršavanja programa na osnovu uslova:

```mermaid
graph TD
    A([Početak if naredbe]) --> B{Provjera uslova}
    B -- True --> C[Blok koda za tačan uslov]
    C --> E([Kraj if naredbe])
    B -- False --> D[Blok koda za netačan uslov]
    D --> E
```

Dijagram toka pokazuje da se program prvo susreće s provjerom uslova. Na osnovu rezultata (`True` ili `False`), prelazi na odgovarajući blok koda (`if` ili `else`). Nakon izvršenja jednog od tih blokova, program nastavlja dalje, prelazeći na naredbe koje slijede izvan strukture `if`-`else`.

Važno je naglasiti da **ne postoji mogućnost u kojoj će se istovremeno izvršiti oba bloka koda**. Na osnovu rezultata logičkog izraza, program uvijek bira samo jednu od dvije alternative.

### Praktični primjeri

Naredba `if`-`else` nalazi primjenu u širokom spektru programskih zadataka gdje je potrebno odabrati jedan od dva moguća toka programa. Sljedeći primjer pokazuje kako se naredba `if`-`else` koristi za provjeru da li je uneseni broj paran ili neparan:

```python=
broj = int(input("Unesite broj: "))

if broj % 2 == 0:
    print(f"Broj {broj} je paran.")
else:
    print(f"Broj {broj} je neparan.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj: 4 <kbd>⏎ Enter</kbd> 
Broj 4 je paran.
</pre> 

<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj: 5 <kbd>⏎ Enter</kbd> 
Broj 5 je neparan.
</pre>

U ovom primjeru logički izraz `broj % 2 == 0` određuje da li je broj paran. Ako je rezultat operacije modulo 0, program ispisuje da je broj paran. U suprotnom, prelazi na blok `else` i ispisuje da je broj neparan.

Naredni primjer koristi naredbu `if`-`else` za donošenje odluka na osnovu starosne granice:

```python=
dob = int(input("Unesite vaše godine: "))

if dob >= 18:
    print("Pristup dozvoljen.")
else:
    print("Pristup odbijen. Morate imati najmanje 18 godina.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite svoju dob: 20 <kbd>⏎ Enter</kbd>
Pristup dozvoljen.
</pre>

<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite svoju dob: 16 <kbd>⏎ Enter</kbd>
Pristup odbijen. Morate imati najmanje 18 godina.
</pre>

Ovaj primjer pokazuje kako se na osnovu unesenih podataka korisniku može pružiti odgovarajuća povratna informacija. Logički izraz `dob >= 18` određuje da li korisnik ispunjava uslov za pristup. Program se ponaša u skladu s rezultatom izraza, birajući odgovarajući blok koda.

Kada program vrši dijeljenje, potrebno je provjeriti da li je nazivnik jednak nuli kako bi se izbjegla greška tokom izvršavanja:

```python=
brojnik = float(input("Unesite brojnik: "))
nazivnik = float(input("Unesite nazivnik: "))

if nazivnik != 0:
    rezultat = brojnik / nazivnik
    print(f"Rezultat dijeljenja je: {rezultat}")
else:
    print("Greška: Dijeljenje s nulom nije dozvoljeno.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite brojnik: 10 <kbd>⏎ Enter</kbd>
Unesite nazivnik: 0 <kbd>⏎ Enter</kbd>
Greška: Dijeljenje s nulom nije dozvoljeno.
</pre>

Primjer iznad koristi logički izraz `nazivnik != 0` kako bi se izbjegle greške. Ako je uslov ispunjen, program računa i ispisuje rezultat. U suprotnom, ispisuje upozorenje i nastavlja izvršavanje bez greške.

Upotrebom naredbe `if`-`else` moguće je provjeriti da li su cifre dvocifrenog broja jednake. Ovaj primjer također pokazuje kako se izdvajaju pojedinačne cifre broja koristeći operacije modulo (`%`) i cjelobrojno dijeljenje (`//`):

```python=
broj = int(input("Unesite dvocifreni broj: "))

jedinice = broj % 10
desetice = broj // 10

if jedinice == desetice:
    print("Cifre su jednake.")
else:
    print("Cifre nisu jednake.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite dvocifreni broj: 99 <kbd>⏎ Enter</kbd>
Cifre su jednake.
</pre>

Operacija `broj % 10` omogućava izdvajanje posljednje cifre dvocifrenog broja (jedinice), tako što koristi ostatak pri dijeljenju broja s 10. S druge strane, operacija `broj // 10` koristi cjelobrojno dijeljenje kako bi uklonila posljednju cifru broja (jedinice), ostavljajući samo prvu cifru koja predstavlja desetice. Ova dva pristupa zajedno omogućavaju efikasno razdvajanje i analizu pojedinačnih cifara broja. Nakon što su cifre izdvojene porede se upotrebom `if desetica == jedinica`, te se na osnovu toga ispisuje odgovarajuća poruka.

## Izrazi i izjave

U Python-u se pravi jasna razlika između **izraza** (eng. expression) i **izjava** (eng statement).

> [!Tip] Definicija
> Izraz je svaki dio koda koji se *evaluira u neku vrijednost*.

Na primjer, matematička operacija `3 + 5` je izraz jer se evaluira u vrijednost `8`.

> [!Tip] Definicija
>Izjava je samostalna instrukcija koja ne mora proizvoditi direktnu vrijednost, već obavlja određenu akciju.

Primjeri akcija uključuju dodjeljivanja vrijednosti (operator dodjele) ili kontrolisanja toka programa (`if`-`else` naredba).

Logički uslov unutar if naredbe predstavlja primjer izraza:

```python=
if broj > 0:
    print("Broj je pozitivan")
```

U ovom slučaju, `broj > 0` je izraz jer se evaluira u vrijednost `True` ili `False`. S druge strane operator dodjele izraz već izjava, jer on ne proizvodi direktnu vrijednost već stvara vezu između identifikatora i vrijednosti.

```python=
if a = True:
    print(True)
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
</pre>

Do istog problema dolazi ukoliko se `if`-`else` naredba pokuša koristiti kao izraz u svom standardnom obliku. Na primjer, sljedeći kod pokušava direktno dodijeliti rezultat `if`-`else` strukture varijabli:

```python=
broj = int(input("Unesite broj: "))
apsolutna_vrijednost = if broj >= 0: broj else: -broj
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
SyntaxError: invalid syntax
</pre>

Ova greška se javlja jer Python standardni `if`-`else` tretira kao izjavu, a ne kao izraz. Ipak, u praksi postoje scenariji gdje bi tretiranje `if`-`else` komande kao izraza olakšalo rješavanje problema i iskazalo željenu namjeru programera na elegantniji način. Kako bi se omogućilo ovakvo ponašanje Python posjeduje **trostruki operator** (eng. ternary operator) koji funkcioniše kao specijalizovni oblik `if`-`else` izraza.

### Trostruki operator

Trostruki operator omogućava evaluaciju uslova i vraćanje vrijednosti u jednoj liniji. Sintaksa trostrukog operatora u Python-u izgleda ovako:

```python
vrijednost = rezultat_ako_tačno if uslov else rezultat_ako_netačno
```
Ova sintaksa može djelovati neobično jer ključna riječ `if` dolazi između dva potencijalna rezultata, što je suprotno standardnoj sintaksi `if`-`else` naredbe. Međutim, ovaj pristup omogućava sažetost i čitljivost u slučajevima kada se radi o jednostavnim uslovima. Na primjer:

```python=
broj = int(input("Unesite broj: "))
apsolutna_vrijednost = broj if broj >= 0 else -broj
print(f"Apsolutna vrijednost je {apsolutna_vrijednost}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj: -42 <kbd>⏎ Enter</kbd>
Apsolutna vrijednost je 42
</pre>

Ovdje, uslov `broj >= 0` određuje koja vrijednost će biti dodijeljena varijabli `apsolutna_vrijednost`. Ako je uslov tačan, vrijednost `broj` se dodjeljuje, dok se u suprotnom dodjeljuje `-broj`.

Kao i svaki drugi izraz trostruki `if`-`else` operator može se koristiti i direktno, bez dodjeljivanja rezultata varijabli:

```python=
print("Broj je pozitivan" if broj > 0 else "Broj nije pozitivan")
```

rezultat trostrukog operatora odmah prosljeđuje funkciji `print`, čime se izbjegava upotreba dodatne varijable.

:::info
Pojam **trostruki operator** tehnički označava svaki operator koji koristi **tri operanda**. Nužno ne implicira strukturu odluke `if`-`else`, već se odnosi na operatore s tri ulazne vrijednosti. Međutim, u većini programskih jezika, termin trostruki operator se najčešće povezuje sa uslovnim operatorom, koji služi kao skraćenica za `if`-`else` izraze.

Primjeri različitih vrsta operatora:
* **Unarni operatori**: Uzimaju jedan operand (npr. `-x`, gdje `-` negira vrijednost `x`).
* **Binarni operatori**: Uzimaju dva operanda (npr. `a + b`, gdje `+` sabira `a` i `b`).
* **Trostruki operatori**: Uzimaju tri operanda (npr. `if`-`else` izraz u Python-u).

U Python-u postoji samo jedan trostruki operator, i to je uslovni operator `if`-`else` u skraćenoj formi.
:::

Jedna od glavnih prednosti trostrukog operatora je *konciznost*. U jednoj liniji koda moguće je izraziti uslov i dvije alternative, čime se smanjuje broj linija potrebnih za implementaciju jednostavnih odluka. Ovo može biti korisno kada je potrebno donijeti odluku u izrazima kao što je inicijalizacija varijable.

Također, ovaj operator može poboljšati *čitljivost* koda u situacijama gdje se odluka lako razumije i gdje je izražena na jasan i logičan način. U takvim slučajevima, trostruki operator omogućava da se fokus održi na logici izraza i izbjegne dodatna struktura tradicionalne `if`-`else` izjave.

Međutim, trostruki operator može značajno smanjiti čitljivost koda ako se koristi za složene uslove. Izraz može postati teško razumljiv, posebno za programere koji nisu upoznati sa sintaksom. Ovo se posebno odnosi na situacije kada se unutar uslovnog operatora ugrađuju složene logičke operacije ili dodatni uslovi.

Druga potencijalna mana je ograničena fleksibilnost. Trostruki operator je osmišljen da obradi samo jednu odluku s dvije alternative, što znači da se ne može koristiti za složenije grane toka programa ili za situacije gdje je potrebno više od dvije opcije.


## Ugniježđene strukture odlučivanja

U složenijim problemima često je potrebno donijeti više odluka koje su međusobno povezane. U takvim situacijama koriste se **ugniježđene** strukture odlučivanja (poznate i kao **umetnute** strukture), gdje se jedna struktura odlučivanja nalazi unutar druge. Ovaj pristup omogućava razradu odluka unutar specifičnog konteksta, pružajući veću kontrolu nad tokom programa.

Umetanje struktura odlučivanja koristi se kada rezultat jedne odluke utiče na logiku ili kriterije za donošenje naredne odluke. Na primjer, prvo se može provjeriti opšti uslov, a zatim, unutar bloka koji odgovara tom uslovu, dalje analizirati dodatne specifične slučajeve.

Iako ugniježđene strukture omogućavaju rješavanje složenih problema, njihova upotreba zahtijeva pažljivu organizaciju koda kako bi se zadržala čitljivost. Prekomjerno umetanje može otežati praćenje toka programa i povećati rizik od grešaka.

### Ugniježđena `if`-`else` struktura

Ugniježđivanje `if`-`else` struktura omogućava donošenje složenijih odluka tako što unutar jednog bloka `if` ili `else` dodajemo dodatnu strukturu odlučivanja. Ova tehnika se koristi kada jedan uslov nije dovoljan za donošenje odluke, već je potrebno razmotriti dodatne kriterije.

Sintaksa ugniježđene strukture izgleda ovako:
```python
if uslov1:
    if uslov2:
        # Blok koda koji se izvršava ako su oba uslova tačna
    else:
        # Blok koda koji se izvršava ako je uslov1 tačan, a uslov2 nije
else:
    # Blok koda koji se izvršava ako uslov1 nije tačan
```

Kod ove strukture, prvo se provjerava `uslov1`. Ako je tačan, prelazi se na provjeru `uslov2`, dok se u suprotnom odmah prelazi na blok koda povezan sa `else`.

Struktura ugniježdenih uslova se može vizualizirati pomoću dijagrama toka izvršavanja programa:

```mermaid
graph TD
    A([Početak if naredbe]) --> B{Provjera uslova 1}
    B -- True --> C{Provjera uslova 2}
    C -- True --> D[Blok koda za tačan uslov 2]
    C -- False --> E[Blok koda za netačan uslov 2]
    D --> H([Kraj if naredbe])
    E --> H
    B -- False --> G[Blok koda za netačan uslov 1]
    G --> H
```

Primjer upotrebe ugniježđene strukture odlučivanja je rješavanje kvadratne jednačine. Kvadratna jednačina ima opšti oblik:

$$
ax^2+bx+c=0
$$

Rješenja ove jednačine određuju se na osnovu diskriminante, koja se računa kao:

$$
D=b^2 - 4ac
$$

Postoji nekoliko slučajeva:
1. Ako je $D>0$, postoje dva različita realna rješenja.
2. Ako je $D=0$, postoji jedno realno rješenje.
3. Ako je $D<0$, nema realnih rješenja.

Kod za rješavanje kvadratne jednačine pomoću umetnute strukture odlučivanja može izgledati ovako:

```python=
a = float(input("Unesite koeficijent a: "))
b = float(input("Unesite koeficijent b: "))
c = float(input("Unesite koeficijent c: "))

if a != 0:
    D = b**2 - 4*a*c
    if D > 0:
        x1 = (-b + D**0.5) / (2 * a)
        x2 = (-b - D**0.5) / (2 * a)
        print(f"Jednačina ima dva rješenja: x1 = {x1}, x2 = {x2}")
    else:
        if D == 0:
            x = -b / (2 * a)
            print(f"Jednačina ima jedno rješenje: x = {x}")
        else:
            print("Jednačina nema realnih rješenja.")
else:
    print("Ovo nije kvadratna jednačina.")
```

Program prvo provjerava da li je $a≠0$, što osigurava da je unesena jednačina zaista kvadratna. Ako jeste, evaluira se diskriminanta i unutar `if` bloka se dalje analiziraju njene vrijednosti. Svaki blok koda unutar umetnute strukture jasno odgovara specifičnom slučaju, čineći logiku rješavanja kvadratne jednačine transparentnom i prilagođenom različitim scenarijima.

U primjeru se kvadratni korijen diskriminante računa korištenjem operatora eksponencijacije `**`. Izraz `D**0.5` podiže vrijednost diskriminante na stepen `0.5`, što je matematički ekvivalent računanju njenog kvadratnog korijena.

:::warning
### Logičke greške zbog pogrešnog poravnanja

Jedan od najvećih izazova kod rada s umetnutim strukturama odlučivanja u Python-u je pravilno poravnanje blokova koda. Python koristi uvlačenje za označavanje blokova koda, pa čak i naizgled mala greška u uvlačenju može uzrokovati pogrešno ponašanje programa. Sljedeći primjeri ilustriraju kako nepravilno uvlačenje može uzrokovati neočekivano ponašanje programa.

U narednom primjeru program pokušava odrediti da li je broj pozitivan, negativan ili nula.

```python=
broj = int(input("Unesite broj: "))

if broj > 0:
    print("Broj je pozitivan.")
if broj < 0:
    print("Broj je negativan.")
else:
    print("Broj je nula.")
```

Prvi `if` uslov nema pripadajuće `else` grane i njegov blok se izvršava kada je ispunjen uslov `broj > 0`, nezavisno od ostatka koda. Navedena `else` grana je povezana sa drugom `if` naredbom i izvršava se kada uslov `broj < 0` nije tačan. To znači da će se tekst `"Broj je nula."` ispisati i kada je unesena vrijednost 0, *ali i kada je vrijednost veća od 0*.

<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj: 42 <kbd>⏎ Enter</kbd>
Broj je pozitivan.
Broj je nula.
</pre>

Ispravna verzija ovog koda je:
```python=
broj = int(input("Unesite broj: "))

if broj > 0:
    print("Broj je pozitivan.")
else:
    if broj < 0:
        print("Broj je negativan.")
    else:
        print("Broj je nula.")
```

U korigovanoj verziji, ukoliko se ustanovi da je unesena vrijednost veća od 0, ispisuje se adekvatna poruka i ne razmatraju se ostale opcije. Tek ako uslov `broj > 0` nije ispunjen se razmatraju ostale mogućnosti koje su sada ugniježdene u `else` granu.
:::

Provjera da li je godina prestupna predstavlja još jedan klasičan primjer upotrebe uslovnih struktura u programiranju. Da bi godina bila prestupna mora ispunjavati sljedeće kriterije:
* Godina je djeljiva sa 4.
* Ako je djeljiva sa 100, mora biti i djeljiva sa 400 da bi bila prestupna.

Na osnovu ovih pravila, može se implementirati logika pomoću `if`-`else` struktura.

```python=
godina = int(input("Unesite godinu: "))

if godina % 4 == 0:
    if godina % 100 == 0:
        if godina % 400 == 0:
            print(f"{godina} je prestupna godina.")
        else:
            print(f"{godina} nije prestupna godina.")
    else:
        print(f"{godina} je prestupna godina.")
else:
    print(f"{godina} nije prestupna godina.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite godinu: 2000 <kbd>⏎ Enter</kbd>
2000 je prestupna godina.
</pre>

Provjera da li je godina prestupna zahtijeva ispitivanje više povezanih uslova. Svaki od navedenih uslova ima tačno određenu svrhu.

1. **Prvi uslov (godina % 4 == 0)**: Provjerava osnovni kriterij za prestupne godine – da li je godina djeljiva sa 4. Ako ovaj uslov nije ispunjen, nema potrebe za daljim provjerama jer godina nije prestupna.
2. **Drugi uslov (godina % 100 == 0)**: Ako je godina djeljiva sa 4, prelazi se na dodatnu provjeru – da li je godina djeljiva sa 100. Ovo je važno jer godine djeljive sa 100 obično nisu prestupne osim u slučaju djeljivosti sa 400.
3. **Treći uslov (godina % 400 == 0)**: Godine koje su djeljive sa 400 predstavljaju izuzetak za pravilo o djeljivosti sa 100. Ako je godina djeljiva sa 400, smatra se prestupnom.
4. **Ispisi**: Svaka grana strukture osigurava odgovarajući ispis, čime se korisniku pruža tačna povratna informacija.

Iako je ovakva implementacija logički tačna, previše umetanja može otežati čitljivost koda. Ovaj zadatak može se riješiti pomoću jedne if-else strukture, eliminacijom viška ugniježđivanja:

```python=
godina = int(input("Unesite godinu: "))

if godina % 4 == 0 and (godina % 100 != 0 or godina % 400 == 0):
    print(f"{godina} je prestupna godina.")
else:
    print(f"{godina} nije prestupna godina.")
```

Korištenjem logičkih operatora (`and` i `or`), svi potrebni uslovi su kombinovani u jedan složen izraz:
* Izraz `godina % 4 == 0` provjerava osnovni uslov djeljivosti sa 4.
* Izraz `(godina % 100 != 0 or godina % 400 == 0)` provjerava dodatni uslov koji osigurava da godine djeljive sa 100 budu prestupne samo ako su djeljive sa 400.

Ovaj pristup koristi istu logiku, ali je bolje organizovan, što ga čini preferiranim rješenjem u većini slučajeva.

Računanje ocjene na osnovu broja osvojenih bodova na predmetu predstavlja primjer problema gdje se logičke odluke granaju u zavisnosti od različitih opsega vrijednosti. Pravila za dodjelu ocjena su jasna – što više bodova student osvoji, to je viša ocjena. Za ovakav zadatak, prirodno rješenje uključuje više uzastopnih provjera vrijednosti osvojenih bodova kako bi se odredila odgovarajuća ocjena.

Rješenje koristeći samo ugniježđene naredbe `if` i `else` može izgledati ovako:

```python=
bodovi = int(input("Unesite broj osvojenih bodova: "))

if bodovi > 94:
    print("Ocjena: 10")
else:
    if bodovi > 84:
        print("Ocjena: 9")
    else:
        if bodovi > 74:
            print("Ocjena: 8")
        else:
            if bodovi > 64:
                print("Ocjena: 7")
            else:
                if bodovi > 54:
                    print("Ocjena: 6")
                else:
                    print("Ocjena: 5")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj osvojenih bodova: 92 <kbd>⏎ Enter</kbd>
Ocjena: 9
</pre>

Ovaj kod ispravno računa ocjenu, ali struktura je nepregledna zbog prekomjernog ugniježđivanja `if` i `else` blokova. Svaka nova provjera dublje ugniježđuje blokove, što otežava razumijevanje toka programa, posebno kada broj grana raste. U ovom slučaju, svaki dodatni uslov zahtijeva još jedno umetanje, a složenost se brzo povećava.

Prekomjerno ugniježđivanje ima nekoliko ključnih problema. Prvo, čitljivost koda se značajno smanjuje. Programer mora pažljivo pratiti uvlačenja kako bi odgonetnuo koja se provjera odnosi na koji uslov. Drugo, greške u uvlačenju mogu dovesti do neočekivanih rezultata, jer je teško vizualno pratiti gdje počinje i završava svaki blok. Konačno, održavanje ovakvog koda postaje problematično – *dodavanje ili uklanjanje* uslova zahtijeva oprezno manipulisanje uvlačenjem i logikom, što povećava rizik od grešaka.


## Naredba `if`-`elif`-`else`

U zadacima gdje je potrebno odabrati između više od dvije mogućnosti, prekomjerno ugniježđivanje `if`-`else` struktura može postati nepregledno i neefikasno. U takvim situacijama Python nudi konstrukciju `elif` (skraćeno od *else if*), koja omogućava višestruki izbor na elegantniji i čitljiviji način. Korištenjem `elif`, program može provjeravati uzastopne uslove bez potrebe za dubljim ugniježđivanjem, pri čemu se svaki uslov evaluira samo ako prethodni nisu ispunjeni.

Sintaksa višestrukog izbora s `elif` izgleda ovako:

```python=
if uslov1:
    # Blok koda koji se izvršava ako je uslov1 tačan
elif uslov2:
    # Blok koda koji se izvršava ako je uslov2 tačan
elif uslov3:
    # Blok koda koji se izvršava ako je uslov3 tačan
else:
    # Blok koda koji se izvršava ako nijedan uslov nije tačan
```
U ovoj strukturi, prvo se provjerava `uslov1`. Ako je tačan, izvršava se pripadajući blok koda, a svi ostali uslovi se preskaču. Ako nije, prelazi se na `elif uslov2`, i tako redom, sve do `else` grane, koja se izvršava ako nijedan od prethodnih uslova nije bio tačan.

Dijagram toka za strukturu `if`-`elif`-`else` vizualno prikazuje tok izvršavanja programa, gdje se uslovi provjeravaju redoslijedom, a odgovarajući blok koda se izvršava čim se pronađe tačan uslov.

```mermaid
graph TD
    A([Početak if-elif-else naredbe]) --> B{Provjera uslova 1}
    B -- True --> C[Blok koda za tačan uslov 1]
    B -- False --> D{Provjera uslova 2}
    D -- True --> E[Blok koda za tačan uslov 2]
    D -- False --> F{Provjera uslova 3}
    F -- True --> G[Blok koda za tačan uslov 3]
    F -- False --> H[Blok koda za else granu]
    C --> I([Kraj if-elif-else naredbe])
    E --> I
    G --> I
    H --> I
```

Struktura višestrukog izbora s `elif` značajno poboljšava čitljivost koda i omogućava linearan tok logike. Svaki `elif` predstavlja alternativni uslov, dok `else` i dalje služi kao zadnja grana koja se izvršava ukoliko nijedan od prethodnih uslova nije bio tačan. Ovo čini logiku programa jasnijom, lakšom za praćenje i manje podložnom greškama, posebno u zadacima gdje je potrebno razmatrati veliki broj različitih slučajeva.

Kada Python naiđe na tačan uslov u strukturi `if`-`elif`, on prestaje provjeravati preostale uslove, čime se poboljšava efikasnost programa. Ovo svojstvo dodatno doprinosi efikasnosti `elif` u odnosu na ugniježđene `if`-`else` strukture, gdje bi svaki blok bio zasebno analiziran.

U primjeru ocjenjivanja na osnovu broja osvojenih bodova, korištenje `elif` naredbe omogućava jasniju i efikasniju implementaciju. Svaka ocjena odgovara određenom opsegu bodova, pri čemu se logika programa prilagođava tako da se svaki naredni uslov evaluira samo ako prethodni nije bio ispunjen.

Rješenje problema ocjenjivanja koristeći elif naredbu može izgledati ovako:

```python=
bodovi = int(input("Unesite broj osvojenih bodova: "))

if bodovi > 94:
    print("Ocjena: 10")
elif bodovi > 84:
    print("Ocjena: 9")
elif bodovi > 74:
    print("Ocjena: 8")
elif bodovi > 64:
    print("Ocjena: 7")
elif bodovi > 54:
    print("Ocjena: 6")
else:
    print("Ocjena: 5")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj osvojenih bodova: 92 <kbd>⏎ Enter</kbd>
Ocjena: 9
</pre>

Svaki `elif` predstavlja alternativni uslov koji se provjerava samo u slučaju da nijedan od prethodnih nije tačan. Na primjer, za unesenih 92 boda, prvi uslov `(bodovi > 94)` neće biti ispunjen, pa program provjerava naredni uslov `(bodovi > 84)`, koji je tačan. Tada se ispisuje ocjena 9, nakon čega se struktura `if`-`elif`-`else` završava.

Jedna od prednosti `elif` struktura je i njihova fleksibilnost pri kasnijem održavanju koda. Dodavanje novih uslova u ovakvu strukturu je jednostavno i ne zahtijeva značajne izmjene u postojećoj logici.

:::warning
Jedna od čestih grešaka pri korištenju `elif` strukture je nepokrivanje svih mogućih slučajeva. Ako se ne doda odgovarajuća `else` grana, program može ostati bez odgovora za određene ulazne vrijednosti, što dovodi do nepredvidivog ponašanja. Na primjer:

```python=
bodovi = int(input("Unesite broj bodova: "))

if bodovi > 90:
    print("Odličan uspjeh")
elif bodovi > 75:
    print("Vrlo dobar uspjeh")
elif bodovi > 60:
    print("Dobar uspjeh")
elif bodovi > 45:
    print("Dovoljan uspjeh")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj bodova: 40 <kbd>⏎ Enter</kbd>
</pre>

Kada se unese vrijednost 40, nijedan od uslova neće biti zadovoljen, pa program neće proizvesti nikakav izlaz. Da bi se izbjegao ovaj problem, potrebno je dodati `else` granu koja obrađuje sve preostale slučajeve:

```python=
else:
    print("Nedovoljan uspjeh")
```

Na ovaj način osigurano je da se za svaki ulazni podatak pruži povratna informacija.

Još jedna greška je postavljanje uslova u pogrešnom redoslijedu, gdje jednostavniji uslovi dolaze prije složenijih. Na primjer:

```python=
broj = int(input("Unesite broj: "))

if broj > 50:
    print("Broj je veći od 50")
elif broj > 90:
    print("Broj je veći od 90")
else:
    print("Broj je manji ili jednak 50")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite broj: 100 <kbd>⏎ Enter</kbd>
Broj je veći od 50
</pre>

Kada se unese broj 100, ispisat će se `"Broj je veći od 50"`, iako je jasno da ispunjava i drugi uslov. Ova greška nastaje jer se uslovi evaluiraju redoslijedom kojim su napisani. Ispravna verzija bi bila:

```python=
if broj > 90:
    print("Broj je veći od 90")
elif broj > 50:
    print("Broj je veći od 50")
else:
    print("Broj je manji ili jednak 50")
```

Sada se ispravni uslovi provjeravaju prema prioritetima, počevši od najsloženijeg ka jednostavnijem.

Konačno, greške u uvlačenju mogu ozbiljno narušiti logiku programa. Na primjer:

```python=
broj = int(input("Unesite broj: "))

if broj > 0:
    print("Broj je pozitivan.")
    elif broj < 0:
        print("Broj je negativan.")
else:
    print("Broj je nula.")
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
SyntaxError: invalid syntax
</pre>

Ovdje je `elif` pogrešno poravnat, pa će Python prijaviti grešku. Ispravna verzija je:

```python=
if broj > 0:
    print("Broj je pozitivan.")
elif broj < 0:
    print("Broj je negativan.")
else:
    print("Broj je nula.")

```
Dodatno, loše poravnanje može dovesti i do neočekivanih rezultata bez prijavljivanja greške, posebno kod složenijih programa. Zbog toga je važno održavati konzistentno uvlačenje u cijelom kodu.

Prethodne greške naglašavaju važnost pažljivog planiranja strukture `elif` naredbi, kao i testiranja programa s različitim ulazima kako bi se osiguralo njihovo pravilno ponašanje.
:::


## Najbolje prakse

Strukture odlučivanja su ključni element u pisanju fleksibilnih i funkcionalnih programa. Međutim, nepravilna implementacija ovih struktura može dovesti do grešaka, smanjene čitljivosti koda ili otežanog održavanja. Da bi se postigla efikasnost i jasnoća, važno je slijediti određene smjernice koje pomažu u organizaciji i optimizaciji logike programa.

Primjenom najboljih praksi, programer osigurava da su sve moguće situacije obuhvaćene, da logika toka programa ostaje jasna i da se izbjegava nepotrebna složenost. U nastavku su navedeni praktični savjeti i preporuke koji olakšavaju pisanje jasnog, efikasnog i pouzdanog koda zasnovanog na strukturama odlučivanja.

1. Jasno i logički organizovani uslovi
    * Osigurati da su uslovi jasno definirani i raspoređeni prema prioritetu. Složeniji ili specifičniji uslovi trebaju biti provjereni prije opštih.
    * Koristiti logičke operatore (`and`, `or`, `not`) za kombinovanje jednostavnijih uslova u složenije izraze, kada je to potrebno.
2. Izbjegavanje prekomjernog ugniježđivanja
    * Previše ugniježđenih struktura čini kod teško čitljivim i sklonim greškama. Kada je moguće, koristiti `elif` za smanjenje dubine ugniježđivanja.
    * Kombinovati logičke uslove kako bi se smanjio broj nivoa ugniježđivanja.
3. Dodavanje `else` grane kada je potrebno
    * Uvijek razmotriti dodavanje `else` grane za situacije kada nijedan od uslova nije ispunjen, posebno kod zadataka gdje se očekuje povratna informacija za svaki mogući ulaz.
    * Koristiti `else` za obradu neočekivanih scenarija kako bi program bio robusniji.
4. Konzistentno uvlačenje i formatiranje
    * Pažljivo održavati uvlačenje koda radi čitljivosti i kako bi se izbjegle greške koje Python prijavljuje kao `IndentationError`.
    * Koristiti prazne linije između većih logičkih dijelova koda kako bi se poboljšala čitljivost.
5. Izbjegavanje redundantnih i suvišnih uslova
    * Provjeriti da li neki uslovi ponavljaju iste logičke provjere unutar strukture. Eliminisati suvišne grane kako bi se optimizovala logika.
6. Jasni nazivi varijabli i komentari
    * Koristiti deskriptivne nazive varijabli kako bi bilo jasno šta određeni uslov provjerava (npr. `godina_je_prestupna` umjesto `x`).
    * Dodavati komentare za složenije odluke, posebno ako se uslovi oslanjaju na specifična pravila ili koncepte.
7. Testiranje svih mogućih grana
    * Provjeriti ponašanje programa za različite ulazne podatke kako bi se osiguralo da svaka grana strukture funkcioniše kako je predviđeno.
8. Upotreba trostrukog operatora za `if`-`else` kao izraz
    * Koristiti trostruki operator za jednostavne uslove koji mogu stati u jednu liniju, ali izbjegavati njegovu upotrebu u složenijim scenarijima jer se može smanjiti čitljivost.

## Sažetak

Strukture odlučivanja predstavljaju ključni mehanizam u programiranju koji omogućava programima da reaguju na različite situacije i prilagode tok izvršavanja na osnovu logičkih uslova. Temelj ovih struktura čini tip podataka `bool`, koji ima dvije vrijednosti: `True` (tačno) i `False` (netačno). Logički operatori `and`, `or` i `not` omogućavaju kreiranje složenih uslova, dok se operatori poređenja koriste za analiziranje odnosa između vrijednosti.

Naredba `if` omogućava izvršavanje bloka koda samo kada je određeni uslov ispunjen, dok naredba `if`-`else` omogućava izbor između dva alternativna toka izvršavanja. Kada je potrebno odabrati između više mogućnosti, koristi se konstrukcija `if`-`elif`-`else`, koja omogućava pregledan i efikasan način organizacije uslova. Korištenje `elif` umjesto ugniježđivanja dodatno poboljšava čitljivost i smanjuje složenost koda.

Ugniježđene strukture odlučivanja omogućavaju donošenje odluka unutar specifičnih konteksta, ali mogu dovesti do nejasnog i teško održivog koda ako nisu pažljivo organizovane. Njihova upotreba zahtijeva jasnu logiku i dosljedno formatiranje kako bi se izbjegle greške, poput nepravilnog uvlačenja redova ili preklapanja uslova.

Korištenje trostrukog operatora (`if`-`else` kao izraz) nudi elegantno rješenje za jednostavne odluke koje zahtijevaju sažetost. Iako trostruki operator poboljšava čitljivost u jednostavnim slučajevima, neprikladan je za složene uslove zbog ograničene fleksibilnosti.

Najbolje prakse uključuju jasno definisane uslove, izbjegavanje redundancije, testiranje svih mogućih scenarija i dosljedno formatiranje. Pravilna organizacija struktura odlučivanja čini programe robusnijim, lakšim za razumijevanje i održavanje, dok istovremeno minimizira rizik od grešaka i povećava njihovu efikasnost.

Strukture odlučivanja predstavljaju temelj adaptivnosti u programiranju, omogućavajući računarima da donose odluke na osnovu podataka i okolnosti. Njihova primjena ne ogleda se samo u jednostavnim zadacima poput provjere uslova, već čini osnovu za složene sisteme, od analize podataka do umjetne inteligencije. Kroz razumijevanje i efikasno korištenje ovih struktura, programeri stiču sposobnost dizajniranja aplikacija koje reagiraju na promjenjive situacije, pružajući rješenja koja su fleksibilna i usklađena s potrebama korisnika.