---
# System prepended metadata

title: Liste
tags: [Programiranje koristeći Python]

---

Liste
=====

U programiranju često se javlja potreba za upravljanjem većim brojem podataka. U prethodnim primjerima, varijable su služile za čuvanje pojedinačnih vrijednosti, poput brojeva ili stringova. Međutim, šta ako je potrebno pratiti više stotina rezultata ispita, unose korisnika ili proizvode u online trgovini? Korištenje zasebnih varijabli za svaku vrijednost nije samo nepraktično već i otežava rad sa podacima. Upravljanje takvim podacima zahtijeva fleksibilniji i efikasniji pristup.

Liste su odgovor na navedene izazove. Ove strukture omogućavaju grupisanje više vrijednosti u jednu cjelinu, čineći ih osnovnim alatom za organizaciju i obradu informacija. Liste pružaju mogućnost dinamičkog upravljanja podacima, što znači da se njihova veličina može mijenjati tokom izvršavanja programa. Umjesto upotrebe desetina ili stotina varijabli za čuvanje podataka, jedna lista može sadržavati sve potrebne podatke, organizovane na pregledan i lako upravljiv način. Ova fleksibilnost čini liste jednim od najvažnijih alata za rad s podacima u Pythonu.

Njihova primjena u svakodnevnim situacijama je veoma široka. Na primjer, lista može predstavljati raspored predavanja za datu sedmicu, niz mjerenja temperature tokom dana, ili čak redoslijed čekanja korisnika u sistemu. Omogućavaju efikasno dodavanje, brisanje i pretraživanje elemenata, čineći ih neizostavnim u svim oblastima programiranja, od analize podataka do razvoja kompleksnih aplikacija.

Kao jedan od osnovnih i univerzalnih tipova podataka u Pythonu, liste olakšavaju organizaciju informacija, smanjujući potrebu za duplikacijom koda. Njihova fleksibilnost i jednostavnost korištenja čine ih idealnim alatom za rješavanje problema koji uključuju rad s velikim količinama podataka. Upravo zbog toga liste zaslužuju posebno mjesto u razumijevanju osnovnih koncepata programiranja.

## Definicija i osnovne karakteristike

Liste su jedan od osnovnih i univerzalno primjenjivih tipova podataka u Pythonu. Predstavljaju strukturirane kolekcije elemenata, organizovane tako da svaki element ima svoj redoslijed i indeks, što omogućava direktan pristup pojedinačnim vrijednostima.

Za razliku od nekih drugih struktura podataka, liste u Pythonu ne nameću ograničenja na tip elemenata koje sadrže. U istoj listi mogu se naći brojevi, stringovi, logičke vrijednosti i drugi objekti. Ova heterogenost omogućava fleksibilnost pri organizaciji podataka, što je posebno korisno u situacijama gdje se različiti tipovi podataka obrađuju zajedno. Na primjer:

```python
podaci = [42, "tekst", 3.14, True]
```

Pored toga veličina listi nije unaprijed definisana. Elementi se mogu dodavati, uklanjati ili mijenjati tokom izvršavanja programa, čineći liste pogodnim za situacije gdje se količina podataka ne može predvidjeti unaprijed. Ova dinamičnost je praćena i mutabilnošću – mogućnošću izmjene sadržaja liste nakon njenog kreiranja, bez potrebe za generisanjem nove kolekcije:

```python
vrijednosti = [1, 2, 3]
vrijednosti.append(4)   # Dodavanje novog elementa
vrijednosti[1] = 5      # Izmjena drugog elementa
print(vrijednosti)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[1, 5, 3, 4]
</pre>

Liste su također optimizovane za razne operacije poput iteracije, sortiranja i filtriranja. Python pruža bogat skup ugrađenih komandi koje omogućavaju jednostavnu manipulaciju njihovim sadržajem, dok posebni operatori i tehnike poput isjecanja (eng. slicing) omogućavaju efikasnu obradu podataka.

Još jedna važna karakteristika lista je njihova sposobnost da sadrže druge liste kao elemente, što omogućava kreiranje višedimenzionalnih struktura. Ove ugniježđene liste koriste se u situacijama kada je potrebno organizovati podatke u matricama, tabelama ili hijerarhijskim strukturama:

```python
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]
```

Osim praktičnih primjena, liste su temelj za razumijevanje i rad s naprednijim strukturama podataka u Pythonu. Zahvaljujući njihovoj svestranosti, često se koriste u implementaciji steka, reda čekanja (eng. queue) i drugih algoritamskih rješenja.

Sve ove osobine čine liste neizostavnim dijelom svakog Python programa. U narednim sekcijama detaljno će biti objašnjeno kako se liste kreiraju, kako im se pristupa, te na koje sve načine možemo manipulisati njihovim sadržajem. Fokus će biti na praktičnim primjerima i tehnikama koje omogućavaju maksimalnu efikasnost i prilagodljivost prilikom rada s listama.


## Kreiranje listi

Liste su osnovni tip podataka u Pythonu i njihovo kreiranje je jednostavno i intuitivno. Zahvaljujući fleksibilnoj sintaksi, moguće je brzo kreirati prazne ili unaprijed popunjene liste, kao i liste s različitim vrstama podataka. Bez obzira na prirodu problema, pravilno kreiranje lista postavlja temelj za njihovo efikasno korištenje u programu.

### Korištenje uglastih zagrada

Najosnovniji način kreiranja liste u Pythonu je korištenje uglastih zagrada `[]`. Unutar zagrada navode se elementi liste, odvojeni zarezima. Ova sintaksa omogućava kreiranje lista s unaprijed poznatim sadržajem, bilo da se radi o homogenim ili heterogenim podacima.

Primjeri kreiranja lista pomoću uglastih zagrada:

```python
cijeli_brojevi = [1, 2, 3, 4, 5]        # Lista cijelih brojeva
realni_brojevi = [3.14, 1.618, 2.71]    # Lista realnih brojeva
vrijednosti = [True, False, None]       # Lista logičkih i specijalnih vrijednosti
razliciti_tipovi = [42, "tekst", 3.14]  # Heterogena lista
```
Jedna od ključnih prednosti listi je što omogućavaju da se više podataka poveže s jednom varijablom. Ovo je značajan napredak u odnosu na pojedinačne varijable, koje mogu čuvati samo jednu vrijednost. Na primjer, varijabla `cijeli_brojevi` sada referencira pet brojeva istovremeno. Svaka lista ima definisani redoslijed elemenata, što omogućava direktan pristup svakom elementu putem njegovog indeksa.

### Kreiranje praznih i unaprijed popunjenih listi

Pored listi s unaprijed definisanim elementima, često postoji potreba za kreiranjem praznih listi, koje će se kasnije popunjavati, ili listi s ponovljenim vrijednostima. Ovi pristupi omogućavaju veliku fleksibilnost pri organizaciji podataka.

#### Prazne liste

Prazne liste koriste se kao polazna tačka za postepeno dodavanje elemenata. Njihovo kreiranje je jednostavno i može se postići na dva načina: korištenjem praznih uglastih zagrada `[]` ili funkcije `list()`:

```python=
prazna_lista_1 = []
prazna_lista_2 = list()
print(prazna_lista_1)
print(prazna_lista_2)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[]
[]
</pre>

Ispis prazne liste prikazuje samo par uglastih zagrada `[]`, što jasno ukazuje da trenutno ne sadrži nijedan element. Takve liste često se koriste kada podaci dolaze postepeno, na primjer, kroz unos u petljama.


#### Liste s ponovljenim vrijednostima

Kada je potrebno kreirati listu s ponovljenim vrijednostima, može se koristiti operator za množenje listi `*`. Ova tehnika omogućava brzo popunjavanje liste istim elementom:

```python=
nule = [0] * 5  # 
tekstovi = ["Python"] * 3 
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[0, 0, 0, 0, 0]
["Python", "Python", "Python"]
</pre>

Operator za množenje listi `*` funkcioniše simetrično, što znači da su izrazi `[0] * 5` i `5 * [0]` potpuno ekvivalentni i vraćaju isti rezultat – listu `[0, 0, 0, 0, 0]`. Ipak, u praksi je uobičajena konvencija koristiti oblik `[0] * 5`, gdje je lista navedena sa lijeve strane operatora, jer je intuitivnije i čitljivije.

Lista s ponovljenim nulama, može biti korisna kada je potrebno brojati više stvari. Na ovaj način može se inicijalizirati više brojača, pri čemu svaki započinje s vrijednošću 0. Ovo je čest pristup u programima gdje se podaci prikupljaju ili analiziraju u odvojenim kategorijama.

### Spajanje listi

Liste se mogu spojiti u novu koristeći operator `+`, koji kreira novu listu kombinovanjem elemenata iz dvije ili više postojećih listi:

```python=
lista1 = [1, 2, 3]
lista2 = [4, 5, 6]
spojena_lista = lista1 + lista2
print(spojena_lista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[1, 2, 3, 4, 5, 6]
</pre>

Operator `+` kreira potpuno novu listu, dok originalne liste ostaju nepromijenjene. Ova osobina je korisna kada je potrebno sačuvati izvornu strukturu podataka, a istovremeno kreirati proširenu verziju.

Ako je potrebno spojiti više listi odjednom, operacija se može ponoviti:

```python=
lista1 = [1, 2, 3]
lista2 = [4, 5, 6]
lista3 = [7, 8, 9]
velika_lista = lista1 + lista2 + lista3
print(velika_lista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[1, 2, 3, 4, 5, 6, 7, 8, 9]
</pre>

Spajanje listi koristi se u situacijama kada je potrebno objediniti podatke iz različitih izvora ili proširiti postojeću kolekciju. Međutim, važno je napomenuti da ova operacija zahtijeva dodatnu memoriju, jer se kreira nova lista, dok originalne ostaju nepromijenjene.

## Pristup elementima

Liste u Pythonu predstavljaju uređene kolekcije podataka gdje redoslijed igra ključnu ulogu.

> [!Tip] Elementi liste
> Svaka pojedinačna vrijednost unutar liste naziva se **element**.

Element može biti bilo koji podatak – broj, string, logička vrijednost, objekat, pa čak i druga lista.

Pristup elementima ključan je za rad s listama jer omogućava njihovo čitanje, ažuriranje ili uklanjanje. Takve operacije su osnova za analizu i manipulaciju podataka unutar listi, bilo da se radi o pretraživanju, sortiranju ili filtriranju elemenata.

Kroz pristup elementima, lista postaje dinamična struktura podataka pogodna za raznovrsne primjene. Na primjer, izdvajanje specifičnih vrijednosti iz liste olakšava dalju obradu, dok izmjene elemenata omogućavaju reakciju na promjene u podacima tokom izvršavanja programa. Fleksibilnost listi direktno proizlazi iz mogućnosti da se lako pristupi i manipuliše njihovim elementima.

### Indeksiranje

Liste u Pythonu organizovane su tako da svaki element ima tačno određeni položaj, što omogućava direktan pristup podacima unutar liste. Taj položaj se označava pomoću indeksa.

>[!Tip] Indeks
>**Indeks** je broj koji označava položaj elementa unutar liste.

U Pythonu, pozitivni indeksi počinju od 0 i redom rastu za svaki naredni element. Ovaj način numerisanja omogućava precizan pristup svakom elementu liste pomoću operatora uglastih zagrada `[]`. Kada se uglaste zagrade nalaze uz varijablu koja pokazuje na lista, Python vraća element koji se nalazi na poziciji navedenoj unutar uglastih zagrada. Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

print(brojevi[0])  # Pristup prvom elementu: 10
print(brojevi[2])  # Pristup trećem elementu: 30
print(brojevi[4])  # Pristup petom elementu: 50

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

Komanda `brojevi[0]` vraća prvi element liste (10), dok `brojevi[4]` vraća posljednji element.

Kada Python obrađuje izraz indeksiranja, izvršava se sljedeći proces:
1.**Identifikacija liste**: Python prepoznaje varijablu brojevi kao listu. Ova lista sadrži niz elemenata koji su pohranjeni u memoriji.
2. **Provjera indeksa**: Navedeni indeks koristi se za određivanje položaja elementa unutar liste. Python broji elemente od 0 za prvi element, 1 za drugi, i tako dalje.
3. **Dohvatanje vrijednosti**: Python pronalazi element na datom položaju i vraća ga kao rezultat operacije.

Proces omogućava efikasan i precizan pristup elementima liste, pružajući osnovu za čitanje i manipulaciju podacima unutar strukture.

>[!Warning] Uglaste zagrade u radu s listama
> Uglaste zagrade `[]` imaju dvije ključne uloge u radu s listama:
>1. Kreiranje listi: Kada se zagrade koriste samostalno, kreiraju listu. Na primjer, `[0]` kreira listu s jednim elementom, vrijednosti 0.
>2. Indeksiranje listi: Kada se zagrade koriste uz varijablu koja čuva listu, koriste se za pristup elementima unutar liste. Na primjer, `brojevi[2]` vraća treći element liste brojevi.
>
> Razlikovanje ove dvije upotrebe ključno je za pravilno razumijevanje rada s listama. Pogrešna primjena zagrada može dovesti do neočekivanih rezultata ili grešaka u programu.


#### Indeksiranje izvan granica liste

Indeksi omogućavaju precizan pristup elementima liste, ali samo dok su unutar granica njene dužine. Ako se pokuša pristupiti indeksu koji ne odgovara nijednom elementu, Python generiše grešku `IndexError`, koja ukazuje da traženi indeks prelazi postojeće granice liste.

```python=
brojevi = [10, 20, 30, 40, 50]

print(brojevi[5])  # Pokušaj pristupa elementu izvan granica
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
IndexError: list index out of range 
</pre>

Ova greška nastaje jer lista `brojevi` sadrži pet elemenata s indeksima od 0 do 4, dok indeks 5 ne postoji.

Da bi se izbjegle greške prilikom indeksiranja, važno je uvijek provjeriti da li se indeks nalazi unutar dozvoljenog raspona. Ovo osigurava stabilnost programa i sprečava neočekivane greške pri radu s listama.

#### Negativni indeksi

Na prvi pogled, moglo bi se očekivati da će indeksiranje liste negativnim brojem, poput -1, izaći izvan opsega liste i generisati grešku `IndexError`. Međutim, Python koristi negativne indekse kao praktičan način za pristup elementima s kraja liste.

```python=
brojevi = [10, 20, 30, 40, 50]

print(brojevi[-1])  # Posljednji element: 50
print(brojevi[-3])  # Treći element s kraja: 30
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
50
30
</pre>

Kada Python obradi izraz `brojevi[-1]`, indeks `-1` pronalazi posljednji element liste. Na sličan način, `brojevi[-3]` odgovara elementu na trećoj poziciji s kraja.

Ipak, ako negativni indeks prelazi veličinu liste (npr. `-6` za listu s pet elemenata), Python generiše grešku `IndexError`.

Kombinacija pozitivnih i negativnih indeksa omogućava fleksibilan pristup podacima, bez obzira na njihovu poziciju u listi.

### Modifikacija elemenata liste

Jedna od ključnih karakteristika listi u Pythonu je njihova **mutabilnost**, što znači da se vrijednosti elemenata mogu mijenjati nakon što lista bude kreirana. Ova osobina omogućava prilagođavanje sadržaja liste prema trenutnim potrebama programa.

Modifikacija se vrši pomoću indeksa. Kada se indeks koristi na lijevoj strani operatora dodjele `=`, Python ažurira vrijednost elementa na datoj poziciji. Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

brojevi[1] = 25  # Zamjena drugog elementa
brojevi[3] = 45  # Zamjena četvrtog elementa

print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 25, 30, 45, 50]
</pre>

U ovom primjeru, vrijednost na indeksu `1` (drugi element) mijenja se iz 20 u 25, dok se vrijednost na indeksu `3` (četvrti element) mijenja iz 40 u 45.

Python omogućava zamjenu elemenata listi vrijednostima različitih tipova. Na primjer, broj u listi može se zamijeniti stringom:

```python=
podaci = [10, "tekst", True]

podaci[0] = "novi tekst"
podaci[2] = 3.14

print(podaci)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
["novi tekst", "tekst", 3.14]
</pre>

U ovom primjeru, broj `10` zamijenjen je stringom `"novi tekst"`, a logička vrijednost`True` realnim brojem `3.14`.

Element liste može se ažurirati na osnovu postojeće vrijednosti:

```python=
brojevi = [10, 20, 30, 40, 50]

brojevi[2] = brojevi[2] * 2
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
 [10, 20, 60, 40, 50]
</pre>

Vrijednost na indeksu `2` (treći element) mijenja se sa `30` na `60`, koristeći izraz `brojevi[2] * 2`. 

>[!Note] Operator množenja
>Operator množenja u ovom primjeru se primjenjuje isključivo na individualni element, povećavajući njegovu vrijednost. To se razlikuje od primjene operatora na listu kao cjelinu, gdje se kreira nova lista s ponovljenim elementima, npr. `[0] * 5`. U datoj situaciji, lista ostaje nepromijenjena, dok se mijenja samo vrijednost jednog elementa unutar nje.

### Brisanje i zamjena elemenata liste

Liste u Pythonu omogućavaju dinamično upravljanje svojim sadržajem kroz dodavanje, uklanjanje ili zamjenu elemenata. Pored metoda specifičnih za liste, koje će biti obrađene u zasebnoj sekciji, Python omogućava uklanjanje elementa liste, kao i zamjenu postojećih vrijednosti direktno.

#### Brisanje elemenata pomoću `del`

Naredba `del` omogućava uklanjanje elemenata liste na osnovu njihovog indeksa. Kada se indeks navede uz naredbu `del`, Python pronalazi odgovarajući element u listi i uklanja ga, dok se svi preostali elementi automatski pomjeraju kako bi popunili nastalu prazninu. Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

del brojevi[2]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 20, 40, 50]
</pre>

Element na indeksu `2` je uklonjen, dok su preostali elementi pomjereni ulijevo kako bi zadržali kontinuitet liste. Brisanje elementa ne ostavlja prazno mjesto u listi.

Naredba del se također koristi za potpuno uklanjanje liste iz memorije:

```python=
brojevi = [10, 20, 30]

del brojevi
print(brojevi)  # Generisat će grešku
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
NameError: name 'brojevi' is not defined
</pre>

Brisanjem cijele liste uklanja se varijabla iz memorije, čime se oslobađa prostor za druge podatke. Pokušaj pristupa obrisanoj listi rezultira greškom jer Python više ne prepoznaje tu varijablu.

Brisanje cijele liste pomoću naredbe `del` ima ključnu ulogu u efikasnom upravljanju memorijom, posebno u programima koji obrađuju velike količine podataka. Kada lista više nije potrebna, njeno zadržavanje zauzima prostor koji bi se mogao koristiti za druge podatke. Uklanjanjem liste oslobađa se taj prostor, omogućavajući programu da funkcioniše s manje opterećenim memorijskim resursima.

Praksa brisanja liste je posebno važna u kontekstu dugotrajnih aplikacija, gdje se tokom izvršavanja nakupljaju mnogi privremeni podaci. Na primjer, tokom analize velikih skupova podataka, privremene liste mogu služiti za međurezultate. Brisanjem tih listi nakon završetka operacija osigurava se da memorija ostane dostupna za druge dijelove programa. Također, u aplikacijama koje rade u okruženjima sa ograničenim resursima, poput ugrađenih sistema ili mobilnih uređaja, optimalno upravljanje memorijom može značajno poboljšati ukupne performanse.

Dakle, brisanje cijele liste nije samo tehnička opcija, već ključni alat za održavanje efikasnosti i stabilnosti programa, omogućavajući resursima da se koriste na najefikasniji način.


#### Zamjena elemenata pomoću privremene varijable

Zamjena elemenata na specifičnim pozicijama često zahtijeva privremeno pohranjivanje jedne od vrijednosti kako bi se izbjegao gubitak podataka. Privremena varijabla koristi se za sigurno čuvanje vrijednosti prije zamjene:

```python=
brojevi = [10, 20, 30, 40, 50]

temp = brojevi[1]
brojevi[1] = brojevi[3] 
brojevi[3] = temp

print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 40, 30, 20, 50]
</pre>

U prikazanom primjeru, vrijednost elementa na drugoj poziciji (20) privremeno se pohranjuje u varijablu `temp`. Nakon toga, vrijednost elementa s četvrte pozicije (40) dodjeljuje se drugom mjestu u listi, čime se originalna vrijednost mijenja. Na kraju, vrijednost iz privremene varijable (20) dodjeljuje se na četvrtoj poziciji, čime je zamjena uspješno okončana. Zamjena vrijednosti uz privremenu varijablu osigurava razmjenjivanje podataka između dva elementa liste, bez preklapanja ili gubitka podataka.


## Ispitivanje svojstava listi

Liste u Pythonu pružaju alate za ispitivanje njihovih svojstava. Dva ključna aspekta koja se često ispituju su prisutnost određenog elementa u listi i broj elemenata koje lista sadrži. Ove operacije su korisne za provjeru sadržaja liste, validaciju unosa ili pripremu za dalju obradu podataka.

### Provjera prisutnosti elemenata u listi

Provjera prisutnosti elementa u listi u Pythonu se ostvaruje pomoću operatora `in`. Ovaj operator pretražuje listu kako bi pronašao odgovarajuću vrijednost i vraća logičku vrijednost `True` ako element postoji, ili `False` ako ne postoji. Operator je jednostavan za korištenje i omogućava čitljive provjere u kodu:

```python=
brojevi = [10, 20, 30, 40, 50]

print(20 in brojevi)
print(60 in brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
True
False
</pre>

Kada se koristi operator `in`, Python provjerava svaki element liste, počevši od prvog, dok ne pronađe traženu vrijednost ili ne dođe do kraja liste. Ako se element pronađe, pretraga se odmah prekida, a rezultat je `True`. U suprotnom, pretraga se završava kada se svi elementi pregledaju, a rezultat je `False`.

Za suprotnu provjeru, Python koristi operator `not in`, koji vraća `True` ako element nije prisutan u listi:

```python=
brojevi = [10, 20, 30, 40, 50]

print(60 not in brojevi)
print(20 not in brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
True
False
</pre>

>[!Warning] Performanse `in` operatora
>Pretraga pomoću `in` operatora prolazi kroz cijelu listu u najgorem slučaju, kada se element ne nalazi u listi. Vrijeme izvršavanja pretrage linearno zavisi od dužine liste, što znači da je za velike liste ova operacija sporija. Ako se često provjerava pripadnost u velikim skupovima podataka, prikladnije je koristiti strukture podataka poput skupova (eng. set), gdje je provjera znatno brža.

### Određivanje veličine liste

Liste u Pythonu imaju promjenjiv broj elemenata, što ih čini fleksibilnim za različite primjene. Za precizno određivanje broja elemenata u listi koristi se ugrađena funkcija `len()`, koja vraća broj elemenata unutar liste kao cijeli broj. Ovo omogućava jednostavno i brzo dobijanje veličine liste, bez potrebe za ručnim prebrojavanjem elemenata.

Funkcija `len()` se koristi tako da se lista navede unutar zagrada funkcije. Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

print(len(brojevi))
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
5
</pre>

Funkcija vraća broj elemenata u listi, uključujući sve vrijednosti bez obzira na njihov tip.

>[!Note] Performanse `len()` funkcije
>Funkcija `len()` koristi internu strukturu liste za brzo dobijanje informacije o njenoj dužini. Python ne prolazi kroz svaki element liste kako bi prebrojao elemente, već koristi optimizovane mehanizme, što ovu operaciju čini vrlo efikasnom čak i za velike liste.

Kada se lista isprazni, funkcija `len()` vraća vrijednost 0, što se može koristi za provjeru da li je lista prazna:

```python=
brojevi = []

if len(brojevi) == 0:
    print("Lista je prazna.")
else:
    print("Lista sadrži elemente.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Lista je prazna.
</pre>


## Iteracija kroz liste

Jedna od najvažnijih osobina lista u Pythonu je mogućnost prolaska kroz sve njene elemente, što omogućava primjenu različitih operacija nad podacima. Ovaj proces se naziva iteracija, a najčešće se realizuje pomoću petlji. Iteracija je osnovna tehnika u programiranju jer omogućava čitanje, analizu, modifikaciju i filtriranje elemenata liste.

Python pruža više načina za iteraciju, pri čemu `for` petlja predstavlja najjednostavniji i najčešće korišten pristup. Uz to, iteracija omogućava direktan pristup elementima liste, ali i kombinaciju s njihovim indeksima, čime se otvaraju dodatne mogućnosti za manipulaciju podacima.

Iteracija se koristi ne samo za osnovne zadatke, već i za rješavanje složenijih problema, poput pronalaženja specifičnih vrijednosti, sortiranja elemenata ili transformacije liste u novu strukturu. U ovoj sekciji biće obrađene ključne tehnike iteracije i njihova primjena kroz konkretne primjere, s naglaskom na praktične aspekte i optimizaciju rada s podacima.


### Osnovna iteracija kroz elemente liste

Iteracija kroz liste u Pythonu najčešće se ostvaruje pomoću `for` petlje, koja omogućava prolazak kroz svaki element liste redom, od prvog do posljednjeg. Ova tehnika pruža direktan pristup svakom elementu, čineći rad s podacima jednostavnim i efikasnim.

Na primjer, `for` petlja može se koristiti za ispis svih elemenata liste:

```python=
brojevi = [10, 20, 30, 40, 50]

for broj in brojevi:
    print(broj)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
10
20
30
40
50
</pre>

Ciljana varijabla `broj` preuzima vrijednost svakog elementa iz liste `brojevi`. Redoslijed iteracije uvijek prati originalni poredak elemenata u listi, od prvog ka posljednjem. Tijelo petlje izvršava zadanu operaciju (u ovom slučaju ispis vrijednosti) za svaki od njih.

Direktna iteracija pomoću `for` petlje:
1. Omogućava rad sa svakim elementom bez potrebe za upravljanjem indeksima.
2. Smanjuje rizik od grešaka vezanih za granice liste ili netačne indekse.
3. Pruža čitljiviji i jednostavniji kod.

Direktna iteracija je najpogodnija u situacijama kada nije potrebno raditi s pozicijama elemenata, već samo s njihovim vrijednostima.


### Pristup indeksima tokom iteracije

Iako direktna iteracija kroz elemente liste često pruža jednostavan i efikasan način rada, postoje situacije kada je pristup indeksima neophodan. Rad sa indeksima omogućava dodatnu kontrolu nad elementima liste, posebno kada je potrebno modifikovati vrijednosti na osnovu njihove pozicije ili raditi sa više listi koje su međusobno povezane.

Korištenjem funkcije `range()`, moguće je generisati niz brojeva koji predstavljaju indekse elemenata u listi. Ovi brojevi se zatim koriste za pristup svakom elementu putem njegovog indeksa. Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

for i in range(len(brojevi)):
    print(f"Element sa indeksom {i} je {brojevi[i]}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Element sa indeksom 0 je 10
Element sa indeksom 1 je 20
Element sa indeksom 2 je 30
Element sa indeksom 3 je 40
Element sa indeksom 4 je 50
</pre>

U prikazanom primjeru, funkcija `len(brojevi)` određuje broj elemenata u listi, dok `range()` generiše niz brojeva od `0` do `len(brojevi) - 1`. Ovi brojevi služe kao indeksi, omogućavajući pristup svakom elementu pomoću sintakse `brojevi[i]`.

>[!Note] Usklađenost funkcija `range()` i `len()`
>Funkcija `range()` ne uključuje krajnju vrijednost, što se zgodno poklapa s radom funkcije `len()` i činjenicom da indeksi liste počinju od `0`. U primjeru iznad, `range(len(brojevi))` generiše validne indekse 0, 1, 2, 3, 4, što obuhvata cijelu listu bez potrebe za dodatnim prilagođavanjem.

Pristup indeksima često se koristi kada je potrebno mijenjati vrijednosti elemenata liste. Na primjer, svaki element u listi može se povećati, duplirati ili na neki način transformisati:

```python=
brojevi = [10, 20, 30, 40, 50]

for i in range(len(brojevi)):
    brojevi[i] = brojevi[i] * 2

print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[20, 40, 60, 80, 100]
</pre>

:::warning
Kada se u `for` petlji koristi ciljana varijabla za prolazak kroz elemente liste, njena promjena ne utiče na originalnu listu. Varijabla unutar petlje djeluje kao **kopija trenutnog elementa**, a ne kao veza sa njim. Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

for broj in brojevi:
    broj *= 2  # Mijenja vrijednost ciljane varijable, ali ne i liste
    print(broj)

print(brojevi)  # Lista ostaje nepromijenjena

```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
20
40
60
80
100
[10, 20, 30, 40, 50]
</pre>

U ovom primjeru, ciljana varijabla `broj` preuzima vrijednost svakog elementa liste tokom iteracije. Međutim, promjena vrijednosti varijable `broj` ne utiče na odgovarajući element liste `brojevi`, jer se radi o nezavisnoj kopiji tog elementa unutar tijela petlje.

Stoga, ukoliko se zaista žele mijenjati vrijednosti elemenata liste, potrebno je iterirati uz pomoć indeksa i direktno pristupiti svakom elementu liste, kao što je prethodno prikazano.
:::

Kada se radi s više listi koje sadrže međusobno povezane podatke, pristup indeksima omogućava jednostavno i precizno povezivanje elemenata na istim pozicijama. Ove liste često predstavljaju različite aspekte iste grupe informacija. Na primjer, u eksperimentu se mogu bilježiti nazivi eksperimenata, rezultati mjerenja i vremena trajanja. Indeksi u ovakvim situacijama služe kao zajednička referenca koja povezuje podatke između listi.

```python=
eksperimenti = ["Eksperiment 1", "Eksperiment 2", "Eksperiment 3"]
rezultati = [23.5, 18.9, 30.1]
trajanja = [120, 95, 140]

for i in range(len(eksperimenti)):
    print(f"{eksperimenti[i]}: {rezultati[i]}, {trajanja[i]} minuta")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Eksperiment 1: 23.5, 120 minuta
Eksperiment 2: 18.9, 95 minuta
Eksperiment 3: 30.1, 140 minuta
</pre>

U ovom primjeru, liste predstavljaju povezane informacije o svakom eksperimentu. Indeks `i` omogućava istovremeni pristup nazivu eksperimenta, rezultatu mjerenja i vremenu trajanja. Na primjer, indeks `0` povezuje ``"Eksperiment 1"``, rezultat `23.5` i trajanje `120` minuta.

Iako funkcionalan, ovaj pristup povezuje podatke na indirektan način, koristeći indekse kao vezu između listi. Ovakva metoda može biti sklona greškama. Na primjer, u situacijama poput sortiranja potrebno je istovremeno sortirati sve liste uz očuvanje njihove međusobne povezanosti. Kako bi se izbjegli takvi problemi, Python nudi i druge strukture podataka koje omogućavaju direktno grupisanje povezanih informacija, kao što su rječnici (eng. dictionaries) koji su prikazani u narednim poglavljima. Ipak, rad s listama i pristup pomoću indeksa i dalje ostaje osnova za razumijevanje složenijih struktura podataka.


### Filtriranje i pretraživanje u listama

Jedna od čestih primjena iteracije kroz liste je pretraživanje i filtriranje elemenata koji zadovoljavaju određene kriterije. Ove operacije omogućavaju identifikaciju specifičnih vrijednosti u listi ili stvaranje novih listi s elementima koji ispunjavaju određene uslove. Iteracija je osnova za implementaciju navedenih zadataka, a pravilna primjena tehnika filtriranja doprinosi efikasnijem upravljanju podacima.

Pretraživanje unutar liste može se obaviti koristeći `for` petlju i uslove unutar tijela petlje. Na primjer, ukoliko je potrebno pronaći sve elemente u listi koji zadovoljavaju određeni kriterij, uslov se koristi za provjeru svakog elementa:

```python=
brojevi = [10, 15, 20, 25, 30]

for broj in brojevi:
    if broj > 20:
        print(broj)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
25
30
</pre>

U primjeru iznad, petlja prolazi kroz sve elemente liste `brojevi` i ispisuje samo one koji su veći od `20`. Kriterij za filtriranje (`broj > 20`) može se prilagoditi potrebama programa.

Kreiranje nove liste koja sadrži samo određene elemente iz postojeće liste često je neophodno za organizaciju i analizu podataka. U Pythonu se ovo najčešće postiže iteracijom i uslovima unutar tijela `for` petlje. U osnovnom pristupu, prazna lista se inicijalizuje prije petlje, a elementi koji zadovoljavaju uslov dodaju se u novu listu koristeći metodu `append()`.

```python=
brojevi = [10, 15, 20, 25, 30]
filtrirani_brojevi = []

for broj in brojevi:
    if broj > 20:
        filtrirani_brojevi.append(broj)

print(filtrirani_brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[25, 30]
</pre>

Lista `filtrirani_brojevi`  sadrži elemente `25` i `30`, koji ispunjavaju kriterij `broj > 20`. Potrebni podaci se izdvajaju bez izmjene originalne liste.

Iteracija omogućava ne samo filtriranje, već i organizaciju elemenata liste u različite grupe na osnovu njihovih karakteristika. Ova tehnika se koristi kada je potrebno kategorizovati podatke ili ih razvrstati prema specifičnim pravilima.

Na primjer, naredni program prikazuje razvrstavanje brojeva u parne i neparne:
```python=
brojevi = [10, 15, 20, 25, 30]
parni = []
neparni = []

for broj in brojevi:
    if broj % 2 == 0:
        parni.append(broj)
    else:
        neparni.append(broj)

print(f"Parni: {parni}")
print(f"Neparni: {neparni}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Parni: [10, 20, 30]
Neparni: [15, 25]
</pre>

Razvrstavanje podataka pruža preglednost i prilagodljivost prilikom analize i daljeg rada sa skupovima podataka. Elementi svake grupe mogu se dodatno obrađivati prema potrebi.


### Kumulativne operacije i transformacije elemenata

Iteracija kroz liste omogućava ne samo prolazak kroz podatke, već i njihovu obradu, transformaciju i prilagođavanje prema potrebama programa. Kroz iteraciju se mogu realizovati kumulativne operacije, poput sumiranja ili množenja elemenata, kao i transformacija podataka radi stvaranja novih listi na osnovu postojećih.

Kumulativne operacije, poput izračunavanja sume ili proizvoda elemenata, koriste iteraciju kako bi postepeno akumulirale rezultate u varijabli. Na primjer, za izračunavanje sume elemenata liste:

```python=
brojevi = [10, 20, 30, 40, 50]

suma = 0
for broj in brojevi:
    suma += broj

print(f"Suma elemenata: {suma}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Suma elemenata: 150
</pre>

Promjena vrijednosti elemenata unutar postojeće liste omogućava prilagođavanje podataka trenutnim potrebama programa. Iteracija kroz indekse liste omogućava direktan pristup svakom elementu i njegovu modifikaciju.

Na primjer, u nastavku je program za udvostručavanje vrijednosti svih elemenata liste:
```python=
brojevi = [10, 20, 30, 40, 50]

for i in range(len(brojevi)):
    brojevi[i] *= 2

print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[20, 40, 60, 80, 100]
</pre>

Izmjene elemenata direktno mijenjaju originalnu listu, što omogućava jednostavniju i efikasniju prilagodbu podataka bez kreiranja dodatnih listi ili struktura podataka.

:::info
#### Sortiranje liste
Sortiranje je jedan od klasičnih problema u oblasti algoritama i struktura podataka. Iako Python posjeduje ugrađenu metodu za sortiranje listi, koja će biti obrađena u nastavku, implementacija vlastitog algoritma za sortiranje korisna je za razumijevanje kako se liste reorganizuju i kako osnovni algoritmi funkcionišu.

Jedan od najpoznatijih algoritama za sortiranje je algoritam **mjehurićastog sortiranja (eng. bubble sort)**[^bubble]. Ovaj algoritam poredi susjedne elemente i zamijeni ih ukoliko nisu u pravilnom redoslijedu. Proces se ponavlja dok cijela lista ne postane sortirana.

[^bubble]: Naziv *bubble sort* dolazi od ideje da se najveći elementi *penju* prema kraju liste kroz iteracije, slično mjehurićima koji se podižu na površinu tečnosti.

Slijedi primjer programa koji sortira listu brojeva koristeći ovaj algoritam:
```python=
brojevi = [64, 34, 25, 12, 22, 11, 90]

for i in range(len(brojevi)):
    for j in range(len(brojevi) - i - 1):
        if brojevi[j] > brojevi[j + 1]:
            temp = brojevi[j]
            brojevi[j] = brojevi[j + 1]
            brojevi[j + 1] = temp

print("Sortirana lista:", brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Sortirana lista: [11, 12, 22, 25, 34, 64, 90]
</pre>

U ovom programu, vanjska petlja kontrolše broj iteracija, dok unutrašnja petlja poredi susjedne elemente i mirjenja ih ukoliko su u pogrešnom redoslijedu. Zamjena elemenata vrši se korištenjem privremene varijable.

Sortiranje liste, bilo korištenjem ugrađenih metoda ili vlastitih algoritama, pruža osnovu za mnoge druge zadatke u programiranju, poput pretraživanja i analize podataka. Primjena vlastitih rješenja doprinosi boljem razumijevanju osnovnih principa rada s podacima.
:::

## Ugrađene metode listi

Python pruža bogat skup alata za rad s listama, koji omogućavaju jednostavno i efikasno upravljanje podacima. Jedan od ključnih aspekata ovih alata su **metode**, specijalne komande koje se izvršavaju direktno nad varijablom koja sadrži listu. Metode omogućavaju dodavanje, uklanjanje, pretraživanje, sortiranje i druge operacije, olakšavajući rad s listama i smanjujući potrebu za ručnim implementacijama tih funkcionalnosti.

Za razliku od uobičajenih funkcija (poput `print()`, `input()`, `len()`) koje se primjenjuju na podatke tako što im se oni prosljeđuju untar zagrada, metode se pozivaju uz ime varijable koja čuva listu, koristeći tačku (`.`) kao separator. Na primjer, metoda `append` omogućava dodavanje novog elementa na kraj liste i koristi se na sljedeći način:

```python=
brojevi = [10, 20, 30]
brojevi.append(40)
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 20, 30, 40]
</pre>

Metode su osmišljene tako da omogućavaju efikasan rad s podacima bez potrebe za složenim algoritmima. Korištenjem ovih ugrađenih alata, zadaci poput proširivanja liste, uklanjanja elemenata ili sortiranja postaju jednostavni za implementaciju i lako razumljivi.

### Metoda `append`

Metoda `append` koristi se za dodavanje novog elementa na kraj liste. Ova metoda je jednostavna za upotrebu i ne zahtijeva dodatne parametre osim vrijednosti koja se dodaje. Primjer primjene već je prikazan u uvod sekcije, gdje je ilustrovano kako se broj 40 dodaje na kraj liste pomoću append metode.

Metoda `append` dodaje element na kraj liste, bez izmjene postojećih elemenata ili redoslijeda. Može se koristiti za dodavanje bilo koje vrijednosti, uključujući brojeve, stringove, logičke vrijednosti, pa čak i druge liste ili složenije strukture podataka.

Na primjer, dodavanje liste kao elementa:

```python=
lista = [1, 2, 3]
lista.append([4, 5])  # Dodaje cijelu listu kao jedan element
print(lista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[1, 2, 3, [4, 5]]
</pre>

Važno je napomenuti da `append` dodaje čitavu listu kao pojedinačni element, a ne spaja njene elemente s originalnom listom.

Metoda `append` je korisna u situacijama kada je potrebno dinamički dodavati elemente tokom izvršavanja programa. Na primjer, prilikom prikupljanja podataka iz korisničkog unosa:

```python=
podaci = []

for i in range(3):
    unos = input("Unesite vrijednost: ")
    podaci.append(unos)

print(podaci)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unesite vrijednost: 10 <kbd>⏎ Enter</kbd>
Unesite vrijednost: 20 <kbd>⏎ Enter</kbd>
Unesite vrijednost: 30 <kbd>⏎ Enter</kbd>
['10', '20', '30']
</pre>

### Metoda `extend`

Metoda `extend` koristi se za dodavanje elemenata iz druge kolekcije (poput liste, skupa ili n-torke skupa)[^n-torka] na kraj postojeće liste. Za razliku od metode `append`, koja dodaje cijeli objekat kao pojedinačni element, `extend` raspakuje elemente kolekcije i dodaje ih kao odvojene elemente liste.

[^n-torka]: Skupovi i n-torke su vrste kolekcija koje su obrađene u narednim poglavljima.

Sintaksa metode extend zahtijeva da se kolekcija navede unutar zagrada. Na primjer:
```python=
brojevi = [10, 20, 30]
brojevi.extend([40, 50, 60])  # Dodaje elemente iz druge liste
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 20, 30, 40, 50, 60]
</pre>

U ovom primjeru, lista `[40, 50, 60]` se raspakuje i njeni elementi se dodaju na kraj liste `brojevi`.

Metoda `extend` korisna je kada je potrebno spojiti sadržaj dvije liste ili dodati elemente iz druge kolekcije, zadržavajući njihov redoslijed. 

### Metoda `insert`

Metoda `insert` koristi se za umetanje novog elementa na tačno **određenu poziciju** unutar liste. Za razliku od metoda `append` i `extend`, koje dodaju elemente na kraj liste, `insert` omogućava precizno pozicioniranje novog elementa, dok se postojeći elementi automatski pomjeraju kako bi napravili prostor za dodatak.

Metoda `insert` prima dva argumenta:
1. **Indeks** – Pozicija na koju se novi element umeće.
2. **Vrijednost** – Element koji se dodaje na zadanu poziciju.

Na primjer:
```python=
brojevi = [10, 20, 30, 40]
brojevi.insert(2, 25)  # Umeće broj 25 na indeks 2
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 20, 25, 30, 40]
</pre>

U ovom primjeru, vrijednost `25` umeće se na indeks `2`. Prethodni elementi ostaju nepromijenjeni, dok se elementi na indeksima `2` i većim pomjeraju za jednu poziciju udesno.

Korištenjem specifične vrijednosti za indeks, metoda `insert` omogućava umetanje elemenata na početak liste:

```python=
brojevi = [10, 20, 30]
brojevi.insert(0, 5)  # Dodaje broj 5 na početak liste
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[5, 10, 20, 30]
</pre>

Metoda `insert` korisna je u situacijama gdje je redoslijed elemenata ključan, a novi podaci moraju biti smješteni na preciznu poziciju. Na primjer, prilikom umetanja stavki u sortiranu listu:

```python=
sortirana_lista = [10, 20, 40, 50]
novi_broj = 30

for i in range(len(sortirana_lista)):
    if sortirana_lista[i] > novi_broj:
        sortirana_lista.insert(i, novi_broj)
        break

print(sortirana_lista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 20, 30, 40, 50]
</pre>

U ovom primjeru, broj `30` je umetnut na odgovarajuće mjesto u sortiranoj listi bez narušavanja njenog redoslijeda.

### Metoda `remove`

Metoda `remove` koristi se za uklanjanje **prvog pojavljivanja** određenog elementa iz liste. Ova metoda traži vrijednost elementa koji treba ukloniti, a ne njegov indeks. Ako se vrijednost pojavljuje više puta, samo prvo pojavljivanje biće uklonjeno.

Metoda prima jedan argument – vrijednost elementa koji treba ukloniti:

```python=
brojevi = [10, 20, 30, 20, 40]
brojevi.remove(20)  # Uklanja prvo pojavljivanje broja 20
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 30, 20, 40]
</pre>

U ovom primjeru, prvo pojavljivanje broja `20` uklonjeno je iz liste, dok ostala pojavljivanja ostaju netaknuta.

:::warning
Ako vrijednost koja se pokušava ukloniti ne postoji u listi, Python generiše grešku `ValueError`. Na primjer:

```python=
brojevi = [10, 20, 30]
brojevi.remove(40)  # Vrijednost 40 ne postoji u listi
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
ValueError: list.remove(x): x not in list
</pre>

Da bi se izbjegle greške, preporučuje se provjera prisutnosti elementa u listi prije pozivanja metode `remove`:

```python=
brojevi = [10, 20, 30]

if 40 in brojevi:
    brojevi.remove(40)
else:
    print("Vrijednost 40 ne postoji u listi.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Vrijednost 40 ne postoji u listi.
</pre>
:::

Metoda `remove` može se koristiti za uklanjanje jedne ili više različitih vrijednosti iz liste. Kada je potrebno eliminisati više elemenata na osnovu njihovih vrijednosti, može se kombinovati iteracija kroz listu vrijednosti za uklanjanje i ponavljano uklanjanje svih pojavljivanja tih vrijednosti iz glavne liste.

Na primjer:
```python=
brojevi = [1, 2, 3, 4, 5, 2, 6, 3]
za_uklanjanje = [2, 3]

for vrijednost in za_uklanjanje:
    while vrijednost in brojevi:
        brojevi.remove(vrijednost)

print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[1, 4, 5, 6]
</pre>

U ovom primjeru:
1. Lista `za_uklanjanje` sadrži vrijednosti `2` i `3`, koje treba ukloniti.
2. Spoljna petlja prolazi kroz svaku vrijednost iz liste `za_uklanjanje`.
3. Unutrašnja petlja uklanja sva pojavljivanja trenutne vrijednosti iz liste `brojevi`.

Upotrebom metode `remove` omogućeno je fleksibilno uklanjanje elemenata iz liste, posebno u situacijama gdje je potrebno eliminisati više različitih vrijednosti. Također, zadržan je originalni redoslijed elemenata koji ne pripadaju grupi za uklanjanje.

### Metoda `pop`

Metoda `pop` koristi se za uklanjanje elementa iz liste **na osnovu njegovog indeksa**. Ova metoda, za razliku od `remove`, ne zahtijeva vrijednost elementa, već koristi indeks kao parametar. Osim uklanjanja, metoda `pop` vraća vrijednost uklonjenog elementa, što omogućava njegovo daljnje korištenje u programu.

Ako indeks nije naveden, metoda `pop` automatski uklanja posljednji element liste:
```python=
brojevi = [10, 20, 30, 40]
uklonjeni = brojevi.pop()  # Uklanja posljednji element
print(brojevi)
print(f"Uklonjena vrijednost: {uklonjeni}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 20, 30]
Uklonjena vrijednost: 40
</pre>

Kada je indeks naveden, metoda uklanja element na toj poziciji:
```python=
brojevi = [10, 20, 30, 40]
uklonjeni = brojevi.pop(1)  # Uklanja element na indeksu 1
print(brojevi)
print(f"Uklonjena vrijednost: {uklonjeni}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 30, 40]
Uklonjena vrijednost: 20
</pre>

:::warning
Ako se koristi indeks koji nije validan (izvan granica liste), Python generiše grešku `IndexError`:

```python=
brojevi = [10, 20, 30]
brojevi.pop(5)  # Indeks 5 ne postoji
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
IndexError: pop index out of range
</pre>

Da bi se izbjegle greške, preporučuje se provjera veličine liste prije korištenja indeksa:
```python=
brojevi = [10, 20, 30]

indeks = 2
if 0 <= indeks < len(brojevi):
    print(f"Uklonjena vrijednost: {brojevi.pop(indeks)}")
else:
    print("Indeks nije validan.")
```
:::

Metoda `pop` često se koristi u implementaciji osnovnih struktura podataka, poput **steka**. Stek je struktura podataka koja slijedi princip **Last In, First Out (LIFO)**, što znači da je posljednji element koji je dodat u stek prvi koji se uklanja. Struktura se koristi se u različitim algoritmima, uključujući analizu izraza, praćenje poziva funkcija i pretragu grafova.

Za rad sa stekom koriste se dvije osnovne operacije:
1. **Dodavanje elementa na stek** – poznato kao operacija *push*.
2. **Uklanjanje elementa sa steka** – poznato kao operacija *pop*.

Liste u Pythonu mogu se koristiti za implementaciju steka, pri čemu se metoda `append` koristi za dodavanje elemenata, a metoda `pop` za njihovo uklanjanje.

Sljedeći primjer prikazuje osnovnu implementaciju steka:

```python=
stek = []  # Prazan stek

# Dodavanje elemenata na stek
stek.append(10)
stek.append(20)
stek.append(30)
print(f"Stek nakon dodavanja: {stek}")

# Uklanjanje elemenata sa steka
while stek:
    print(f"Skidanje sa steka: {stek.pop()}")

print(f"Stek nakon uklanjanja: {stek}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Stek nakon dodavanja: [10, 20, 30]
Skidanje sa steka: 30
Skidanje sa steka: 20
Skidanje sa steka: 10
Stek nakon uklanjanja: []
</pre>

U primjeru, elementi `10`, `20` i `30` dodaju se na stek pomoću metode `append`, čime se redom smještaju na kraj liste. Metoda `pop` koristi se za uklanjanje elemenata, pri čemu se svaki put uklanja posljednji dodani element, što odgovara principu rada steka. Nakon što se svi elementi uklone stek ostaje prazan.

> [!Note] Tačne (*truthy*) i netačne (*falsy*) vrijednosti za liste
>U Pythonu, liste se smatraju *truthy* vrijednostima ako sadrže **barem jedan element**, dok su prazne liste *falsy*. Ovo znači da se lista tretira kao `True` u logičkim izrazima sve dok nije prazna.
>
>U primjeru iznad, petlja `while` se izvršava dok lista `stek` nije prazna. Kada se uklone svi elementi iz liste, ona postaje prazna (`[]`) i evaluira se kao `False`, čime se petlja zaustavlja.
>
>Ova osobina omogućava elegantno korištenje listi u logičkim uslovima.

### Metoda `index`

Metoda `index` koristi se za pronalaženje indeksa **prvog pojavljivanja** određenog elementa u listi. Ova metoda vraća cijeli broj koji predstavlja poziciju elementa, a pretraga počinje od početka liste i završava čim se pronađe prvi traženi element.

Metoda prima jedan obavezni argument – vrijednost elementa čiji se indeks traži:
```python=
brojevi = [10, 20, 30, 20, 40]
indeks = brojevi.index(20)  # Pronalazi indeks prvog pojavljivanja broja 20
print(f"Indeks vrijednosti 20 je: {indeks}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Indeks vrijednosti 20 je: 1
</pre>

U ovom primjeru, metoda `index` vraća `1`, jer je `20` pronađeno po prvi put na toj poziciji.

Metoda `index` podržava dodatne argumente koji omogućavaju precizniju kontrolu pretrage:
* **Start**: Početni indeks za pretragu.
* **Stop**: Krajnji indeks (ne uključuje ovaj indeks u pretragu).

Na primjer:
```python=
brojevi = [10, 20, 30, 20, 40]
indeks = brojevi.index(20, 2)  # Pretraga počinje od indeksa 2
print(f"Indeks vrijednosti 20 nakon indeksa 2 je: {indeks}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Indeks vrijednosti 20 nakon indeksa 2 je: 3
</pre>

Pretraga u ovom primjeru počinje od indeksa `2`, zbog čega metoda vraća `3` kao poziciju drugog pojavljivanja vrijednosti `20`.

:::warning
Ako tražena vrijednost ne postoji u listi, Python generiše grešku `ValueError`. Na primjer:

```python=
brojevi = [10, 20, 30]
brojevi.index(40)  # Vrijednost 40 ne postoji u listi
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
ValueError: 40 is not in list
</pre>

Da bi se izbjegle greške, preporučuje se provjera prisutnosti elementa u listi prije pozivanja metode `index`:

```python=
brojevi = [10, 20, 30]

if 40 in brojevi:
    indeks = brojevi.index(40)
    print(f"Indeks vrijednosti 40 je: {indeks}")
else:
    print("Vrijednost 40 ne postoji u listi.")
```
:::

Metoda `index` korisna je u situacijama gdje je potrebno pronaći poziciju elementa i izvršiti dalju obradu koja zavisi od njegovog položaja. Na primjer, zamjena određene riječi u listi:

```python=
rijeci = ["python", "programiranje", "lista", "metode"]
ciljana_rijec = "lista"

if ciljana_rijec in rijeci:
    indeks = rijeci.index(ciljana_rijec)
    rijeci[indeks] = "LISTA"  # Zamjena pronađene riječi
    print(rijeci)
else:
    print(f"Riječ '{ciljana_rijec}' nije pronađena u listi.")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
['python', 'programiranje', 'LISTA', 'metode']
</pre>

U primjeru, metoda `index` koristi se za određivanje pozicije riječi `"lista"` unutar liste. Nakon što je pronađena, riječ na tom indeksu zamjenjuje se novom vrijednošću, čime se sadržaj liste prilagođava na osnovu identifikovane pozicije.

### Metoda `sort`

Metoda `sort` koristi se za sortiranje elemenata liste prema zadanim pravilima. U Pythonu, metoda sortira listu u rastućem redoslijedu. Ova metoda radi *na licu mjesta* (eng. in-place), što znači da mijenja originalnu listu bez stvaranja nove.

Za osnovno sortiranje liste nije potrebno navesti dodatne parametre. Na primjer:
```python=
brojevi = [40, 10, 30, 20]
brojevi.sort()  # Sortira listu u rastućem redoslijedu
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 20, 30, 40]
</pre>

Lista `brojevi` sada je sortirana od najmanjeg prema najvećem elementu.

Metoda `sort` podržava opcionalni parametar `reverse`, koji omogućava sortiranje liste u opadajućem redoslijedu. Kada je `reverse=True`, elementi se redaju od najvećeg prema najmanjem:
```python=
brojevi = [40, 10, 30, 20]
brojevi.sort(reverse=True)  # Sortira listu u opadajućem redoslijedu
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[40, 30, 20, 10]
</pre>

:::warning
Metoda `sort` radi s elementima koji su međusobno uporedivi. Na primjer, brojevi i stringovi mogu se pojedinačno sortirati, ali pokušaj sortiranja liste koja sadrži različite tipove podataka rezultiraće greškom:
```python=
pomijesano = [10, "tekst", 20]
pomijesano.sort()  # Greška: Neporedivi tipovi podataka
```
<pre style="background-color: #1e1e1e; color: #FF4C4C; padding: 20px;">
TypeError: '&lt;' not supported between instances of 'str' and 'int' 
</pre>
:::

Metoda `sort` često se koristi za organizaciju podataka prije njihove analize ili prikaza. Na primjer, u nastavku je kod za sortiranje liste ocjena:

```python=
ocjene = [85, 90, 75, 95, 80]
ocjene.sort()
print(f"Sortirane ocjene: {ocjene}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Sortirane ocjene: [75, 80, 85, 90, 95]
</pre>

### Metoda `reverse`

Metoda `reverse` koristi se za obrtanje redoslijeda elemenata u listi. Ova metoda također radi *na licu mjesta* (eng. in-place). Za razliku od metode `sort`, metoda `reverse` ne sortira elemente već ih samo preokreće.

Sintaksa metode `reverse` ne zahtijeva dodatne argumente:
```python=
brojevi = [10, 20, 30, 40]
brojevi.reverse()  # Preokreće redoslijed elemenata u listi
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[40, 30, 20, 10]
</pre>

Lista brojevi sada ima redoslijed elemenata obrnut u odnosu na originalni.

Metoda `reverse` često se koristi u kombinaciji s drugim metodama za postizanje specifičnih rezultata. Na primjer, sortiranje liste u opadajućem redoslijedu može se postići kombinovanjem metoda `sort` i `reverse`:
```python=
brojevi = [10, 40, 30, 20]
brojevi.sort()
brojevi.reverse()
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[40, 30, 20, 10]
</pre>

Iako se u ovom primjeru ista operacija može postići korištenjem parametra `reverse=True` u metodi `sort`, kombinacija metoda ilustruje kako se `reverse` može upotrijebiti za obradu već sortiranih podataka.

Metoda `reverse` može se koristiti za analizu podataka kada je potrebno pristupiti elementima u obrnutom redoslijedu. Na primjer, ako lista predstavlja dnevne temperature zabilježene tokom sedmice, metoda `reverse` može se koristiti za analizu podataka počevši od najsvježijih ka najstarijim:
```python=
temperature = [15, 18, 20, 22, 19, 17, 16]

print("Temperature po redoslijedu dana:")
for dan in range(len(temperature)):
    print(f"Dan {dan + 1}: {temperature[dan]}°C")

temperature.reverse()  # Preokreće redoslijed temperatura

print("\nTemperature od posljednjeg dana prema prvom:")
for dan in range(len(temperature)):
    print(f"Dan {len(temperature) - dan}: {temperature[dan]}°C")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Temperature po redoslijedu dana:
Dan 1: 15°C
Dan 2: 18°C
Dan 3: 20°C
Dan 4: 22°C
Dan 5: 19°C
Dan 6: 17°C
Dan 7: 16°C

Temperature od posljednjeg dana prema prvom:
Dan 7: 16°C
Dan 6: 17°C
Dan 5: 19°C
Dan 4: 22°C
Dan 3: 20°C
Dan 2: 18°C
Dan 1: 15°C
</pre>

Prvi ispis prikazuje temperature od ponedjeljka do nedjelje. Nakon što metoda `reverse` preokrene redoslijed, temperature se prikazuju od nedjelje prema ponedjeljku.


## Rezanje listi

Liste u Pythonu omogućavaju ne samo pristup pojedinačnim elementima, već i izdvajanje većih dijelova, što se naziva **rezanje liste** (eng. list slicing). Ova tehnika omogućava izdvajanje podlisti na osnovu definisanog raspona indeksa, što je korisno za selektivnu obradu podataka ili kreiranje novih listi.

Rezanje lista koristi sintaksu koja uključuje početni i krajnji indeks, odvojene dvotačkom (`:`). Na primjer:
```python=
brojevi = [10, 20, 30, 40, 50]
podlista = brojevi[1:4]
print(podlista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[20, 30, 40]
</pre>

Krajnji indeks nije uključen u podlistu. Rezanje listi može se koristiti i sa dodatnim opcijama, poput preskakanja elemenata ili izdvajanja podataka od početka do kraja liste.

### Osnovna sintaksa rezanja lista

Rezanje liste koristi sintaksu `lista[pocetak:kraj]`, gdje su:
* `pocetak` – indeks prvog elementa koji će biti uključen u podlistu. Ako se izostavi, podrazumijevana vrijednost je `0`.
* `kraj` – indeks koji označava kraj raspona, ali nije uključen u podlistu. Ako se izostavi, podrazumijeva se kraj liste.

Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

# Elementi od indeksa 1 do 3 (4 je isključen)
podlista = brojevi[1:4]
print(podlista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[20, 30, 40]
</pre>

Ako se izostavi početni ili krajnji indeks, podrazumijevaju se granice liste:
```python=
brojevi = [10, 20, 30, 40, 50]

# Elementi od početka do indeksa 2 (3 je isključen)
podlista = brojevi[:3]
print(podlista)

# Elementi od indeksa 2 do kraja
podlista = brojevi[2:]
print(podlista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 20, 30]
[30, 40, 50]
</pre>

### Negativni indeksi u rezanju lista

Kao i kod pristupa pojedinačnim elementima, negativni indeksi omogućavaju rezanje liste s kraja prema početku. Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

# Posljednja tri elementa
podlista = brojevi[-3:]
print(podlista)

# Elementi između trećeg i pretposljednjeg
podlista = brojevi[2:-1]
print(podlista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[30, 40, 50]
[30, 40]
</pre>

Negativni indeksi olakšavaju rad s podacima kada se želi pristupiti dijelovima liste bez potrebe za eksplicitnim računanjem njihovih položaja u listi.


### Korak pri rezanju listi

Rezanje liste omogućava dodatnu fleksibilnost korištenjem parametra **korak** (eng. step), koji definiše razmak između odabranih elemenata. Sintaksa za uključivanje koraka je `lista[pocetak:kraj:korak]`. Podrazumijevana vrijednost koraka je `1`.

Na primjer:
```python=
brojevi = [10, 20, 30, 40, 50]

# Svaki drugi element od indeksa 0 do 4 (5 isključen)
podlista = brojevi[0:5:2]
print(podlista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 30, 50] 
</pre>

Korak je posebno koristan za analize i selekciju podataka u pravilnim intervalima. Na primjer, izdvajanje parnih elemenata:

```python=
brojevi = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Parni brojevi
parni = brojevi[1::2]
print(parni)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[2, 4, 6, 8, 10]
</pre>

#### Negativni korak pri rezanju liste

Korištenje negativne vrijednosti za korak omogućava biranje elemenata liste unazad, počevši od krajnjeg prema početnom indeksu. Kada se koristi negativan korak, ponašanje rezanja liste mijenja se na sljedeći način:
* **Početni indeks** (`pocetak`) - treba biti veći od krajnjeg indeksa, jer se kreće unazad kroz listu. Ako se izostavi, podrazumijevan je posljednji element liste.
* **Krajnji indeks** (`kraj`) - treba biti manji od početnog indeksa. Ako se izostavi, podrazumijevan je početak liste.

Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

# Elementi od indeksa 3 prema indeksu 1 (indeks 1 nije uključen)
podlista = brojevi[3:1:-1]
print(podlista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[40, 30]
</pre>

U ovom primjeru, elementi liste biraju se unazad, počevši od predposljednjeg elementa (indeks `3`) prema trećem elementu (indeks `2`), dok indeks `1` nije uključen.

Ako se početni i krajnji indeks izostave, negativni korak koristi se za biranje svih elemenata liste u obrnutom redoslijedu, što se može koristiti kao jednostavna alternativa za preokretanje liste. Na primjer:

```python=
brojevi = [10, 20, 30, 40, 50]

# Obrće redoslijed elemenata
obrnuta_lista = brojevi[::-1]
print(obrnuta_lista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[50, 40, 30, 20, 10]
</pre>

Za razliku od metode `reverse`, koja mijenja originalnu listu, rezanje s negativnim korakom kreira novu listu, ostavljajući originalnu nepromijenjenom.

### Zamjena dijela liste korištenjem rezanja

Rezanje listi omogućava ne samo izdvajanje podlisti, već i direktnu zamjenu elemenata unutar definisanog raspona. Ova tehnika pruža način za ažuriranje sadržaja liste bez potrebe za složenim algoritmima.

Kada broj novih elemenata odgovara broju elemenata koji se zamjenjuju, rezanje funkcioniše intuitivno:
```python=
brojevi = [10, 20, 30, 40, 50]

# Zamjena elemenata od indeksa 1 do 3
brojevi[1:4] = [25, 35, 45]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 25, 35, 45, 50]
</pre>

Ako broj novih elemenata ne odgovara broju onih koji se zamjenjuju, lista se automatski prilagođava. Na primjer, kada se broj novih elemenata poveća dobije se izlaz:

```python=
brojevi = [10, 20, 30, 40, 50]

# Zamjena dijela liste s više elemenata
brojevi[1:3] = [21, 22, 23, 24]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 21, 22, 23, 24, 40, 50]
</pre>

Kada se broj novih elemenata smanji rezultat je:
```python=
brojevi = [10, 20, 30, 40, 50]

# Zamjena dijela liste s manjim brojem elemenata
brojevi[1:4] = [25]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 25, 50]
</pre>

Rezanje listi podržava i upotrebu koraka prilikom zamjene, što omogućava ažuriranje samo određenih elemenata u definisanom rasponu:

```python=
brojevi = [10, 20, 30, 40, 50]

# Zamjena svakog drugog elementa u rasponu
brojevi[::2] = [15, 35, 55]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[15, 20, 35, 40, 55]
</pre>


### Brisanje elemenata iz liste korištenjem `del` i rezanja

Naredba `del` omogućava uklanjanje elemenata iz liste na osnovu njihovih indeksa. U kombinaciji s rezanjem listi, `del` se može koristiti za brisanje više elemenata odjednom, čime se lista dinamički prilagođava bez potrebe za dodatnim strukturama.

Kada se koristi rezanje, naredba del uklanja sve elemente unutar definisanog raspona:
```python=
brojevi = [10, 20, 30, 40, 50]

# Brisanje elemenata od indeksa 1 do 3
del brojevi[1:4]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[10, 50]
</pre>

U ovom primjeru, elementi `20`, `30` i `40` uklanjaju se iz liste, dok preostali elementi ostaju nepromijenjeni.

Kombinovanjem `del` i rezanja koje obuhvata cijelu listu, moguće je isprazniti listu:
```python=
brojevi = [10, 20, 30, 40, 50]

# Brisanje svih elemenata
del brojevi[:]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">

</pre>

Lista `brojevi` postaje prazna, ali sama varijabla `brojevi` ostaje definisana i može se ponovo koristiti.

Korištenjem koraka moguće je selektivno ukloniti određene elemente unutar liste:
```python=
brojevi = [10, 20, 30, 40, 50, 60, 70]

# Brisanje svakog drugog elementa
del brojevi[::2]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[20, 40, 60]
</pre>

U ovom primjeru, uklanjaju se elementi na indeksima `0`, `2`, `4` i `6`, dok ostali elementi ostaju u listi.

Za razliku od metoda poput `remove` ili `pop`, naredba `del` koristi indekse za brisanje elemenata i omogućava brisanje više elemenata odjednom. Ovaj pristup je efikasan za brzo uklanjanje većih dijelova liste bez potrebe za dodatnim petljama.


## Ugniježdene liste

Liste u Pythonu mogu sadržavati ne samo jednostavne podatke poput brojeva i stringova, već i druge liste kao svoje elemente. Ove liste, poznate kao ugniježdene liste, omogućavaju organizaciju podataka u višedimenzionalne strukture, što ih čini pogodnim za rad sa složenijim skupovima informacija, poput tabela ili matrica.

Ugniježdene liste pružaju fleksibilnost za rad sa podacima koji imaju hijerarhijsku ili višeslojnu strukturu. Svaki element ugniježdene liste može se pristupiti kombinacijom indeksa, pri čemu se svaki indeks odnosi na određeni nivo ugniježdene strukture.

Na primjer:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Pristup drugom redu
print(matrica[1])  # [4, 5, 6]

# Pristup elementu u drugom redu, na drugoj koloni
print(matrica[1][1])  # 5
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[4, 5, 6]
5
</pre>

### Pristup elementima ugniježdene liste

Kako je prikazano u uvodu sekcije, elementima ugniježdene liste može se pristupiti kombinovanjem indeksa, pri čemu prvi indeks odabire unutrašnju listu, a drugi cilja određeni element unutar te liste.

Osim pojedinačnih elemenata, moguće je izdvajanje podlisti iz ugniježdene strukture. Na primjer, može se izdvojiti cijeli red ili samo određeni elementi unutar reda:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Izdvajanje drugog i trećeg elementa drugog reda
podlista = matrica[1][1:]
print(podlista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[5, 6]
</pre>

U ovom primjeru, koristi se rezanje liste kako bi se izdvojili elementi unutar specifičnog reda. Indeks `1` bira drugi red matrice, dok `[1:]` cilja sve elemente od drugog do kraja.

Kada je potrebno izdvojiti sve elemente iz određene kolone, koriste se petlje za iteraciju kroz sve unutrašnje liste:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Izdvajanje prve kolone
kolona = []
for red in matrica:
    kolona.append(red[0])

print(kolona)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[1, 4, 7]
</pre>

Ovdje svaka iteracija uzima prvi element iz svakog reda matrice (`red[0]`) i dodaje ga u novu listu `kolona`.

Ugniježdene liste ne moraju imati uniformnu strukturu. Svaka unutrašnja lista može imati različit broj elemenata, što omogućava rad s nestrukturisanim podacima. Na primjer:

```python=
razlicite_duzine = [
    [1, 2],
    [3, 4, 5],
    [6]
]
# Pristup posljednjem elementu svake unutrašnje liste
posljednji_elementi = []
for red in razlicite_duzine:
    if red:
        posljednji_elementi.append(red[-1])

print(posljednji_elementi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[2, 5, 6]
</pre>

Primjer prikazuje kako se mogu obraditi ugniježdene liste koje nisu iste dužine. Provjera unutar petlje (`if red`) osigurava da se prazne liste preskoče, čime se izbjegavaju greške prilikom pristupa.

Ugniježdene liste omogućavaju efikasnu pretragu podataka koristeći kombinaciju petlji i logičkih uslova. Na primjer:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

ciljani_broj = 5
prisutno = False
for red in matrica:
    if ciljani_broj in red:
        prisutno = True
        break

print(f"Broj {ciljani_broj} je prisutan: {prisutno}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Broj 5 je prisutan: True
</pre>

U ovom primjeru, vrši se provjera svake unutrašnje liste za prisustvo ciljanog broja. Čim se broj pronađe, postavlja se logička varijabla prisutno na True, a petlja se prekida pomoću break.

### Iteracija kroz ugniježdene liste

Iteracija kroz ugniježdene liste omogućava pristup svim elementima unutar višedimenzionalne strukture. Iteracija se koristi za obradu podataka, modifikaciju elemenata ili analizu.

Za pristup pojedinačnim elementima ugniježdene liste koristi se ugniježdena petlja. Vanjska petlja prolazi kroz unutrašnje liste, dok unutrašnja petlja prolazi kroz njihove elemente:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

print("Elementi matrice:")
for red in matrica:
    for element in red:
        print(element, end=" ")
    print()
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Elementi matrice:
1 2 3 
4 5 6 
7 8 9
</pre>

Iteracija kroz ugniježdene liste može se koristiti i za direktnu modifikaciju njihovih elemenata. Na primjer, svi elementi matrice mogu se povećati za određeni iznos:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

for i in range(len(matrica)):
    for j in range(len(matrica[i])):
        matrica[i][j] += 10

print("Modifikovana matrica:")
for red in matrica:
    print(red)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Modifikovana matrica:
[11, 12, 13]
[14, 15, 16]
[17, 18, 19]
</pre>

U ovom primjeru, petlje koriste indekse `i` i `j` za pristup svakom elementu matrice. Ovaj pristup je koristan kada je potrebno izvršiti aritmetičke ili druge promjene na svim elementima unutar ugniježdene strukture.

Ugniježdene petlje u kombinaciji s logičkim uslovima omogućavaju selektivnu obradu elemenata. Na primjer, identifikacija svih parnih brojeva u matrici:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

print("Parni brojevi u matrici:")
for red in matrica:
    for element in red:
        if element % 2 == 0:
            print(element, end=" ")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Parni brojevi u matrici:
2 4 6 8 
</pre>

U primjeru unutrašnja petlja uključuje logički uslov `if element % 2 == 0`, koji provjerava da li je broj paran. Elementi koji zadovoljavaju ovaj uslov ispisuju se redom.

### Modifikacija ugniježdenih listi

Ugniježdene liste ne služe samo za čitanje podataka već omogućavaju i njihovu modifikaciju. Promjene mogu uključivati ažuriranje pojedinačnih elemenata, zamjenu cijelih unutrašnjih listi ili dodavanje i uklanjanje elemenata. Modifikacija ugniježdenih listi često se koristi za dinamičko prilagođavanje podataka u strukturama poput tabela ili matrica.

Cijele unutrašnje liste mogu se zamijeniti novim vrijednostima, što omogućava veće izmjene u ugniježdenoj strukturi:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Zamjena drugog reda
matrica[1] = [40, 50, 60]
print("Matrica nakon zamjene reda:")
for red in matrica:
    print(red)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Matrica nakon zamjene reda:
[1, 2, 3]
[40, 50, 60]
[7, 8, 9]
</pre>

Ovaj pristup omogućava brzo ažuriranje cijelih dijelova strukture.

Unutrašnje liste ugniježdene strukture mogu se proširiti pomoću metoda poput `append` ili `extend`:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Dodavanje novog elementa u drugi red
matrica[1].append(70)
print("Matrica nakon dodavanja elementa:")
for red in matrica:
    print(red)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Matrica nakon dodavanja elementa:
[1, 2, 3]
[4, 5, 6, 70]
[7, 8, 9]
</pre>

Elementi se mogu ukloniti iz unutrašnjih listi koristeći `del`, `remove` ili `pop`:

```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Uklanjanje elementa iz drugog reda
del matrica[1][1]
print("Matrica nakon uklanjanja elementa:")
for red in matrica:
    print(red)

```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Matrica nakon uklanjanja elementa:
[1, 2, 3]
[4, 6]
[7, 8, 9]
</pre>

Ove tehnike omogućavaju prilagodbu ugniježdenih lista u skladu sa zahtjevima aplikacije, bilo da je riječ o mijenjanju pojedinačnih vrijednosti, dodavanju novih podataka ili uklanjanju postojećih.

### Dinamičko kreiranje ugniježdenih listi

Dinamičko kreiranje ugniježdenih listi omogućava generisanje višedimenzionalnih struktura, poput matrica ili tabela, koje mogu imati proizvoljne dimenzije i početne vrijednosti. Posebno je korisno kada dimenzije i sadržaj liste zavise od korisničkog unosa ili specifičnih zahtjeva programa.

Pomoću ugrađenih petlji moguće je generisati matricu sa unaprijed definisanim vrijednostima za svaki element. Na primjer, kreiranje matrice sa početnim vrijednostima `0`:
```python=
redovi = 3
kolone = 4

# Kreiranje matrice dimenzija 3x4
matrica = []
for i in range(redovi):
    red = []
    for j in range(kolone):
        red.append(0)
    matrica.append(red)

print("Matrica sa početnim vrijednostima 0:")
for red in matrica:
    print(red)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Matrica sa početnim vrijednostima 0:
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
</pre>

U ovom primjeru, vanjska petlja iterira kroz broj redova (`redovi`), pri čemu za svaki red kreira praznu listu `red`. Unutrašnja petlja popunjava tu listu dodavanjem unaprijed definisanih vrijednosti (`0`). Kada unutrašnja petlja završi dodavanje svih elemenata za jedan red, cijeli red se dodaje u glavnu listu `matrica`. Ovaj proces ponavlja se za svaki red dok matrica ne dobije željene dimenzije.

Dinamičko kreiranje omogućava generisanje matrice gdje su vrijednosti elemenata određene njihovim rednim brojem ili pozicijom:

```python=
redovi = 3
kolone = 3

# Kreiranje matrice gdje je svaki element jednak zbroju svojih indeksa
matrica = []
for i in range(redovi):
    red = []
    for j in range(kolone):
        red.append(i + j)
    matrica.append(red)

print("Matrica sa vrijednostima zasnovanim na indeksima:")
for red in matrica:
    print(red)

```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Matrica sa vrijednostima zasnovanim na indeksima:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
</pre>

Kod u ovom primjeru koristi unutrašnju petlju za generisanje vrijednosti elemenata matrice na osnovu zbira njihovih indeksa (`i + j`). Indeks `i` predstavlja trenutni red, dok indeks `j` odgovara trenutnoj koloni. Na ovaj način, svaki element matrice sadrži jedinstvenu vrijednost koja zavisi od njegove pozicije u strukturi. Kada se red popuni, dodaje se u matricu, a proces se ponavlja dok svi redovi ne budu generisani.

### Ugniježdene liste s višestrukim nivoima

Ugniježdene liste nisu ograničene na dva nivoa (npr. redovi i kolone), već mogu imati i više nivoa ugniježdenosti. Takve strukture koriste se za predstavljanje složenijih podataka, poput trodimenzionalnih matrica, višeslojnih hijerarhija ili čak reprezentacije slika.

Višedimenzionalne liste sastoje se od unutrašnjih listi koje opet mogu sadržavati druge liste. Svaki nivo ugniježdenosti dodaje dodatnu dimenziju strukturi, čime se omogućava rad s kompleksnijim podacima. Na primjer:

```python=
trodimenzionalna_lista = [
    [
        [1, 2], [3, 4]
    ],
    [
        [5, 6], [7, 8]
    ]
]

print(trodimenzionalna_lista[1][1][0])
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
7
</pre>

U ovom primjeru, lista ima tri nivoa, gdje svaki indeks cilja određeni nivo ugniježdenosti: prvi indeks bira blok, drugi red, a treći element u redu.

Trodimenzionalne liste pružaju mogućnost skladištenja i organizacije podataka koji imaju više nivoa povezanosti. Jedan praktičan primjer je praćenje minimalnih i maksimalnih temperatura za više gradova tokom nekoliko dana.[^druge_opcije] Prva dimenzija predstavlja gradove, druga dane, a treća dimenzija sadrži minimalnu i maksimalnu temperaturu za svaki dan:

[^druge_opcije]: Python nudi i druge strukture podataka, poput rječnika, koji omogućavaju pregledniju i intuitivniju organizaciju ovakvih podataka. Na primjer, gradovi se mogu predstaviti kao ključevi rječnika, dok bi dani i temperature bili organizovani u ugniježdene rječnike ili liste. Međutim, u ovom primjeru fokus je na trodimenzionalnim listama kako bi se ilustrovala njihova primjena u radu sa složenim podacima.

```python=
# Dimenzije
broj_gradova = 2
broj_dana = 3
min_max = 2  # Minimalna i maksimalna temperatura

# Kreiranje trodimenzionalne liste s početnim vrijednostima 0
temperature = []
for grad in range(broj_gradova):
    grad_temperatura = []
    for dan in range(broj_dana):
        grad_temperatura.append([0, 0])  # Minimalna i maksimalna temperatura
    temperature.append(grad_temperatura)

# Dodavanje izmjerenih vrijednosti
temperature[0][0] = [10, 20]  # Grad 1, Dan 1: min=10, max=20
temperature[0][1] = [12, 22]  # Grad 1, Dan 2: min=12, max=22
temperature[1][0] = [8, 18]   # Grad 2, Dan 1: min=8, max=18

# Prikaz podataka
print("Podaci o minimalnim i maksimalnim temperaturama:")
grad_index = 1
for grad in temperature:
    print(f"Grad {grad_index}:")
    dan_index = 1
    for dan in grad:
        print(f"  Dan {dan_index}: Min={dan[0]}, Max={dan[1]}")
        dan_index += 1
    grad_index += 1
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Podaci o minimalnim i maksimalnim temperaturama:
Grad 1:
  Dan 1: Min=10, Max=20
  Dan 2: Min=12, Max=22
  Dan 3: Min=0, Max=0
Grad 2:
  Dan 1: Min=8, Max=18
  Dan 2: Min=0, Max=0
  Dan 3: Min=0, Max=0
</pre>

U ovom primjeru, trodimenzionalna lista `temperature` koristi se za skladištenje podataka o minimalnim i maksimalnim temperaturama za dva grada tokom tri dana. Vanjska petlja kreira listu za svaki grad, srednja dodaje listu za dane, a unutrašnja popunjava minimalne i maksimalne temperature s početnim vrijednostima `0`.

Ažuriranje podataka vrši se pomoću indeksa. Na primjer, `temperature[0][0]` predstavlja minimalnu i maksimalnu temperaturu u prvom gradu za prvi dan. Prikaz podataka realizovan je iteracijom kroz svaki nivo liste, uz jasno označavanje gradova i dana.

>[!Note] Veza višedimenzionalnih listi i dubokog učenja: Tenzori
Višedimenzionalne liste u Pythonu mogu poslužiti kao osnovni koncept za razumijevanje tenzora, ključne strukture u dubokom učenju i mašinskom učenju. Tenzori su generalizacija vektora (jednodimenzionalnih) i matrica (dvodimenzionalnih) na višedimenzionalne podatke.
>
>Na primjer, trodimenzionalna lista koja sadrži RGB vrijednosti piksela slike dimenzija 64x64 može se smatrati trodimenzionalnim tenzorom. 
>
>U okviru biblioteka za duboko učenje, poput TensorFlow ili PyTorch, tenzori su osnovni tip podataka koji omogućava efikasne matematičke operacije i obradu velikih skupova podataka na grafičkim procesorima (GPU). Iako Pythonove liste nisu optimizovane za ovakve operacije, razumijevanje višedimenzionalnih listi pruža osnovu za rad s tenzorima u naprednijim bibliotekama.

#### Određivanje dimenzija višedimenzionalnih listi

Određivanje dimenzija višedimenzionalnih listi ključno je za analizu i obradu podataka, posebno kada se radi s velikim ili kompleksnim strukturama. Dimenzije liste definišu broj elemenata na svakom nivou ugniježdenosti:
* Prva dimenzija odgovara broju unutrašnjih listi.
* Druga dimenzija predstavlja broj elemenata unutar svake unutrašnje liste, i tako dalje.
Za određivanje dimenzija koristi se ugrađena funkcija `len()`.

Za liste s dvije dimenzije (matrice), dimenzije se mogu jednostavno odrediti:
```python=
matrica = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

broj_redova = len(matrica)
broj_kolona = len(matrica[0]) if broj_redova > 0 else 0

print(f"Dimenzije matrice: {broj_redova}x{broj_kolona}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Dimenzije matrice: 3x3
</pre>

Ovdje `len(matrica)` određuje broj redova, dok `len(matrica[0])` mjeri broj kolona u prvom redu.

Za liste s više od dvije dimenzije potrebno je provjeriti dužinu na svakom nivou ugniježdenosti:

```python=
trodimenzionalna_lista = [
    [
        [1, 2], [3, 4]
    ],
    [
        [5, 6], [7, 8]
    ]
]

broj_blokova = len(trodimenzionalna_lista)
broj_redova = len(trodimenzionalna_lista[0]) if broj_blokova > 0 else 0
broj_kolona = len(trodimenzionalna_lista[0][0]) if broj_redova > 0 else 0

print(f"Dimenzije liste: {broj_blokova}x{broj_redova}x{broj_kolona}")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Dimenzije liste: 2x2x2
</pre>

Određivanje dimenzija ključno je za mnoge algoritme, jer omogućava pravilno rukovanje podacima, posebno kada se radi s generičkim funkcijama ili kompleksnim strukturama.


## Kopiranje listi u Pythonu

Kopiranje listi u Pythonu ključno je pitanje u programiranju, jer omogućava pravljenje novih verzija liste za dalju obradu, analizu ili modifikaciju, bez uticaja na originalne podatke. Međutim, nije uvijek dovoljno samo kreirati novu referencu na istu listu, jer promjene na jednoj mogu neplanirano uticati na drugu.

Intuitivno, moglo bi se očekivati da operator dodjele (`=`) funkcioniše za kopiranje lista na isti način kao i za druge tipove podataka. Na primjer, kopiranje integera:

```python=
a = 5
b = a  # Kopiranje integera
b = 10

print("Vrijednost a:", a)
print("Vrijednost b:", b)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Vrijednost a: 5
Vrijednost b: 10
</pre>

Promjena vrijednosti varijable `b` nema uticaja na varijablu `a`, jer se cijeli brojevi u Pythonu kopiraju direktno kao nezavisne vrijednosti.

Međutim, situacija sa listama je drugačija. Kada se lista dodijeli novoj varijabli, ne kreira se nova kopija liste, već samo nova referenca na istu listu. To znači da promjene na jednoj listi automatski utiču na drugu:

```python=
lista1 = [1, 2, 3, 4]
lista2 = lista1  # Dodjela reference

lista2[0] = 10  # Promjena prvog elementa liste2

print("Originalna lista:", lista1)
print("Dodijeljena lista:", lista2)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Originalna lista: [10, 2, 3, 4]
Dodijeljena lista: [10, 2, 3, 4]
</pre>

Promjena vrijednosti u listi `lista2` reflektuje se i na `lista1`, jer obje varijable ukazuju na istu memorijsku lokaciju. Ova situacija poznata je kao *aliasing*, gdje dvije ili više varijabli dijele isti objekat u memoriji. Naziv aliasing potiče od engleske riječi alias (nadimak), koja reflektuje činjenicu da obje varijable *nadimkom* pokazuju na isti objekat. Ovaj mehanizam se često koristi za efikasnost, jer izbjegava nepotrebno kopiranje podataka, ali zahtijeva dodatnu pažnju kako bi se spriječile nenamjerne promjene originalne liste.

Da bi se izbjegao aliasing i omogućilo nezavisno rukovanje originalnom listom i njenom kopijom, neophodno je kreirati novu listu koja neće dijeliti istu memorijsku lokaciju kao original. U Pythonu postoji više načina za kopiranje lista, što omogućava programerima da odaberu najprikladniji pristup u zavisnosti od konteksta i čitljivosti koda.

Sljedeći primjer prikazuje različite tehnike za kopiranje liste i kako se one ponašaju:

```python=
lista1 = [1, 2, 3, 4]

# Različiti načini kopiranja lista
lista2 = list(lista1)      # Korištenje funkcije list()
lista3 = lista1.copy()     # Korištenje metode copy()
lista4 = [] + lista1       # Korištenje operatora +
lista5 = lista1[:]         # Korištenje rezanja [:]

# Modifikacija kopiranih lista
lista2[0] = 10
lista3[1] = 20
lista4[2] = 30
lista5[3] = 40

# Prikaz rezultata
print("Originalna lista:", lista1)
print("Lista 2 (list):", lista2)
print("Lista 3 (copy):", lista3)
print("Lista 4 (+):", lista4)
print("Lista 5 (rezanje):", lista5)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Originalna lista: [1, 2, 3, 4]
Lista 2 (list): [10, 2, 3, 4]
Lista 3 (copy): [1, 20, 3, 4]
Lista 4 (+): [1, 2, 30, 4]
Lista 5 (rezanje): [1, 2, 3, 40]
</pre>

U ovom primjeru svaka metoda kreira novu listu koja je nezavisna od originalne. Promjene na kopijama (`lista2`, `lista3`, `lista4`, `lista5`) ne utiču na `lista1`, što potvrđuje da je aliasing uspješno izbjegnut.

Python nudi ove različite načine kopiranja listi kako bi omogućio fleksibilnost programerima. Funkcija list() nudi čitljiv način pravljenja kopije i pogodna je za rad sa različitim kolekcijama koje se mogu konvertovati u listu. Metoda copy(), dodata u novijim verzijama Pythona, ističe se kao najjasniji i najintuitivniji način jer jasno pokazuje namjeru kopiranja.

Operator + i rezanje [:] pružaju alternativne opcije, ali mogu biti manje čitljivi za početnike jer njihov cilj nije eksplicitno kopiranje, već se koristi kao sporedni efekat njihovog ponašanja. Uprkos tome, svi navedeni pristupi su funkcionalno ekvivalentni i generišu plitke kopije liste.

Postojanje više načina za kopiranje listi rezultat je evolucije Pythonovog jezika i različitih potreba koje se javljaju u radu s podacima. U ranim verzijama Pythona nije postojala specifična metoda za kopiranje listi, pa su programeri koristili funkciju `list()` ili rezanje `[:]`. Kasnijim verzijama uvedena je metoda `copy()`, čime je proces učinjen čitljivijim i intuitivnijim. Operator `+` dodan je kao univerzalna sintaksa za kombinovanje i kopiranje listi, iako nije eksplicitno dizajniran za ovu svrhu.

Svaki od ovih pristupa ima svoju svrhu i mjesto u Python kodu:
1. Metoda `copy()` uvedena je kako bi proces kopiranja bio eksplicitniji i intuitivniji. Njeno ime jasno ukazuje na namjenu, čime poboljšava čitljivost koda. Ovo je Pythonovski način za kopiranje listi i **preferira se u modernom pisanju koda** jer precizno definiše svrhu.
2. Funkcija `list()` primarno je osmišljena za konverziju drugih tipova podataka u liste, slično kao što `int()` konvertuje u cijele brojeve, a `float()` u decimalne vrijednosti. Međutim, kada se koristi sa listama, funkcija takođe kreira novu kopiju liste. Ovaj način je univerzalan i može se koristiti u kontekstu rada s različitim tipovima podataka, ali njena sintaksa nije specifično fokusirana na kopiranje lista.
3. Rezanje `[:]` pruža brz i sažet način za kopiranje liste. Iako je funkcionalno ekvivalentno metodi `copy()`, njegova namjena nije odmah očigledna za nekoga ko nije upoznat s tehnikom rezanja. Rezanje se često koristi u kraćim programima ili kada se naglasak stavlja na minimalizam u sintaksi.
4. Operator `+` primarno je dizajniran za spajanje listi, ali se može iskoristiti i za kreiranje kopije. Spajanjem prazne liste sa postojećom (`[] + lista1`) dobija se nova lista koja sadrži elemente originalne. Međutim, zbog toga što njegova osnovna namjena nije kopiranje, može djelovati nejasno u ovom kontekstu.

Bez obzira na odabranu tehniku, svi navedeni načini kreiraju nezavisne kopije koje osiguravaju sigurno rukovanje podacima i izbjegavanje problema aliasinga.

### Efekti prosljeđivanja listi funkcijama

Kada se liste prosljeđuju funkcijama u Pythonu, one se prenose prema referenci, što znači da funkcija radi direktno sa originalnim objektom. Promjene napravljene na listi unutar funkcije reflektuju se na originalnu listu izvan funkcije. Ovo ponašanje je specifično za mutabilne tipove podataka, poput listi.

Na primjer:
```python=
def dodaj_element(lista, element):
    lista.append(element)
    print("Unutar funkcije:", lista)

# Originalna lista
moja_lista = [1, 2, 3]

# Prosljeđivanje liste funkciji
dodaj_element(moja_lista, 4)

print("Izvan funkcije:", moja_lista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unutar funkcije: [1, 2, 3, 4]
Izvan funkcije: [1, 2, 3, 4]
</pre>

Funkcija `dodaj_element` dodaje vrijednost `4` u listu `moja_lista`. Budući da funkcija radi s originalnom listom, promjene su vidljive i izvan njenog tijela. Ovo ponašanje može biti korisno, ali zahtijeva pažnju kako bi se spriječile nenamjerne promjene na originalnim podacima.

Ako funkcija treba raditi s listom, ali ne smije mijenjati originalne podatke, preporučuje se kreiranje kopije liste prije prosljeđivanja funkciji:

```python=
def dodaj_element(lista, element):
    lista.append(element)
    print("Unutar funkcije:", lista)

# Originalna lista
moja_lista = [1, 2, 3]

# Prosljeđivanje kopije liste funkciji
dodaj_element(moja_lista.copy(), 4)

print("Izvan funkcije:", moja_lista)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Unutar funkcije: [1, 2, 3, 4]
Izvan funkcije: [1, 2, 3]
</pre>

Korištenjem `moja_lista.copy()`, kreira se kopija liste koja se prosljeđuje funkciji. Na taj način, funkcija može raditi s kopijom bez uticaja na originalnu listu.


### Duboko kopiranje listi

Kod rada sa listama u Pythonu, plitko kopiranje (korištenjem metoda poput `copy()` ili rezanja `[:]`) kopira samo prvi sloj liste, dok unutrašnje liste ili objekti ostaju povezani sa originalom. Ovo znači da promjene napravljene u unutrašnjim listama kopije utiču i na originalnu listu:

```python=
matrica1 = [[1, 2], [3, 4]]

# Plitko kopiranje koristeći copy()
matrica2 = matrica1.copy()

# Promjena elementa u kopiji
matrica2[0][0] = 99

print("Originalna matrica:", matrica1)
print("Plitko kopirana matrica:", matrica2)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Originalna matrica: [[99, 2], [3, 4]]
Plitko kopirana matrica: [[99, 2], [3, 4]]
</pre>

U ovom primjeru, i `matrica1` i `matrica2` dijele istu unutrašnju listu. Promjena u jednom elementu kopije reflektuje se i na original.

Duboko kopiranje se koristi za kreiranje nezavisnih kopija na svim nivoima liste. U Pythonu, modul `copy` nudi funkciju `deepcopy()` za ovu svrhu:

```python=
import copy

matrica1 = [[1, 2], [3, 4]]

# Duboko kopiranje koristeći deepcopy()
matrica2 = copy.deepcopy(matrica1)

# Promjena elementa u kopiji
matrica2[0][0] = 99

print("Originalna matrica:", matrica1)
print("Duboko kopirana matrica:", matrica2)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Originalna matrica: [[1, 2], [3, 4]]
Duboko kopirana matrica: [[99, 2], [3, 4]]
</pre>

U ovom primjeru, `deepcopy()` kreira potpuno nezavisnu kopiju matrice. Promjene na kopiji nemaju efekta na original.

uboko kopiranje neophodno je u radu s ugniježdenim listama ili složenim strukturama gdje je nezavisnost između originala i kopije ključna. Omogućava sigurno rukovanje podacima tako što u potpunosti kopira svaki nivo strukture, čime promjene na kopiji ne utiču na original.

Ipak, duboko kopiranje zahtijeva više vremena i resursa jer prolazi kroz cijelu strukturu i kreira nove objekte za svaki nivo. Za jednostavne liste ili plitke strukture, plitko kopiranje često je dovoljno i znatno efikasnije. Odabir između plitkog i dubokog kopiranja zavisi od složenosti podataka i specifičnih zahtjeva programa.

>[!Note] Promjenjivost i nepromjenjivost u programiranju
U Pythonu, liste su promjenjive (eng. mutable), što znači da se njihovi elementi mogu mijenjati nakon što su kreirane. Ovo omogućava fleksibilnost i efikasnost, jer promjene na listama ne zahtijevaju kreiranje potpuno novih objekata. Međutim, promjenjivost nosi rizik neželjenih efekata, poput neočekivanih promjena prilikom prosljeđivanja listi funkcijama.
>
>S druge strane, neki programski jezici, poput Haskella i Clojurea, favorizuju nepromjenjive (immutable) strukture podataka. U ovim jezicima, jednom kreirane strukture poput listi ne mogu se mijenjati. Svaka promjena generiše novu verziju strukture, dok original ostaje netaknut. Ovakav pristup smanjuje rizik grešaka povezanih s dijeljenjem referenci, olakšava razumijevanje toka podataka i poboljšava performanse.
>
>Python takođe podržava nepromjenjive strukture podataka, poput n-torki, koje se koriste kada je potrebna sigurnost od promjena. Odabir između promjenjivih i nepromjenjivih struktura zavisi od specifičnih potreba aplikacije, ali razumijevanje ovih koncepata ključno je za pisanje robusnog i predvidivog koda.

## Stvaranje listi sintaksom

Sintaksa za stvaranje list (eng list comprehension) predstavlja elegantan i čitljiv način za kreiranje novih listi u Pythonu. Ova tehnika omogućava generisanje listi primjenom izraza unutar jednostavne sintakse, često zamjenjujući klasične petlje i funkcije za modifikaciju podataka.

Na primjer, klasična petlja za generisanje kvadrata brojeva:

```python=
kvadrati = []
for x in range(5):
    kvadrati.append(x**2)
```

može se zamijeniti sintaksom za stvaranje listi:
```python=
kvadrati = [x**2 for x in range(5)]
print(kvadrati)
```

Sintaksa za stvaranje listi je:
```python
[izraz for element in lista]
```

Prvi dio je **izraz**, koji određuje kako će svaki element nove liste izgledati. Ovo može biti jednostavna operacija, poput množenja `(x*2)`, ili složeniji izraz koji uključuje funkcije i računske operacije. Drugi dio je **element**, privremena varijabla koja redom preuzima vrijednosti iz kolekcije podataka, u ovom slučaju **liste**. Svaki element liste prolazi kroz definisani izraz i rezultat se dodaju u novu listu.

Sintaksa se može proširiti dodavanjem uslova za filtriranje elemenata. U ovom slučaju, dodaje se opcionalni dio sa `if` izrazom:
```python
[izraz for element in lista if uslov]
```

Ovaj prošireni oblik omogućava generisanje liste koja sadrži samo elemente koji zadovoljavaju određeni uslov.

Jedna od čestih primjena sintakse za stvaranje listi je **filtriranje podataka**. Na primjer, ako postoji potreba da se izdvoje svi parni brojevi iz određene kolekcije, sintaksa za stvaranje listi omogućava direktno uključivanje uslova u procesu generisanja nove liste:

```python=
parni = [x for x in range(10) if x % 2 == 0]
print(parni)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[0, 2, 4, 6, 8]
</pre>

Druga česta primjena je transformacija podataka, gdje se na svaki element kolekcije primjenjuje neka operacija. Na primjer, pretvaranje svih brojeva iz liste u njihove kvadrate može se lako postići:

```python=
kvadrati = [x**2 for x in range(5)]
print(kvadrati)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[0, 1, 4, 9, 16]
</pre>

U ovom primjeru, se svaki broj iz intervala od 0 do 4 kvadrira i dodaje u listu.

Još jedna korisna primjena je konverzija tipova podataka. Pretvaranje liste stringova u brojeve često je potrebno u radu sa podacima. Sintaksa za stvaranje listi omogućava jednostavnu konverziju:
```python=
stringovi = ["1", "2", "3", "4"]
brojevi = [int(x) for x in stringovi]
print(brojevi)
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
[1, 2, 3, 4]
</pre>

U ovom primjeru, lista stringova `["1", "2", "3", "4"]` konvertovana je u listu brojeva `[1, 2, 3, 4]`. Ovaj pristup se često koristi pri obradi ulaznih podataka ili konverziji podataka iz vanjskih izvora poput fajlova.

Sintaksa za stvaranje listi nudi veliku fleksibilnost i čitljivost, omogućavajući istovremeno transformaciju i filtriranje podataka. Korištenjem ove tehnike kod postaje kraći i jasniji, dok se smanjuje potreba za složenim petljama i dodatnim strukturama.

## Liste kao ogledalo memorijskog modela računara

Dok strukture ponavljanja demonstriraju snagu procesora u izvršavanju velikog broja operacija, liste u Pythonu omogućavaju razumijevanje načina na koji računar upravlja memorijom.

Pythonove liste su implementirane tako da omogućavaju fleksibilno dodavanje i uklanjanje elemenata. Kada se lista kreira, Python rezerviše određeni prostor u memoriji za smještanje elemenata. Ako lista premaši dodijeljeni prostor, Python automatski alocira veću memorijsku lokaciju i kopira postojeće elemente u novu oblast. Ovaj proces se naziva dinamička alokacija memorije.

Za ilustraciju, sljedeći kod pokazuje kako se veličina liste mijenja tokom dodavanja elemenata:

```python=
import sys

lista = []
print(f"Početna veličina liste: {sys.getsizeof(lista)} bajtova")

for i in range(5):
    lista.append(i)
    print(f"Dodano {i}, veličina: {sys.getsizeof(lista)} bajtova")
```
<pre style="background-color: #1e1e1e; color: white; padding: 20px;">
Početna veličina liste: 56 bajtova
Dodano 0, veličina liste: 88 bajtova
Dodano 1, veličina liste: 88 bajtova
Dodano 2, veličina liste: 88 bajtova
Dodano 3, veličina liste: 88 bajtova
Dodano 4, veličina liste: 120 bajtova
</pre>

Može se uočiti da veličina liste ne raste linearno sa svakim dodavanjem elemenata. Python unaprijed rezerviše dodatni prostor u memoriji kako bi smanjio učestalost premještanja elemenata, čime optimizuje rad sa listama.

Tokom rada sa listama, memorijsko opterećenje računara može se posmatrati pomoću alata specifičnih za operativni sistem. Ovaj proces može se najbolje razumjeti pokretanjem programa koji dodaje veliki broj elemenata u listu:

```python=
import sys

lista = []
print(f"Početna veličina liste: {sys.getsizeof(lista)} bajtova")

# Velika petlja za dodavanje elemenata
for i in range(1_000_000_000):
    lista.append(i)
    if i % 10_000_000 == 0:
        print(f"Broj elemenata: {i}, veličina: {sys.getsizeof(lista)} bajtova")

print("Dodavanje elemenata završeno.")
```

* **Windows**:
    * Otvoriti *Task Manager* pomoću  <kbd>⌃ Control</kbd> + <kbd>⇧ Shift</kbd> + <kbd>⎋ Escape</kbd>.
    * Na kartici *Performance* pratiti graf memorijske upotrebe tokom izvršavanja programa.
* **MacOS**:
    * Pokrenuti *Activity Monitor* (pretraga pomoću *Spotlighta* za "Activity Monitor").
    * Na kartici *Memory* pratiti kako memorijska potrošnja raste tokom rada programa.
* **Linux**:
    * U terminalu koristiti naredbu `top` ili `htop` za prikaz trenutne zauzetosti memorije.

Praćenjem memorijskog opterećenja tokom izvršavanja programa koji intenzivno koristi liste, može se direktno uočiti veza između softverskih struktura podataka i hardverskog upravljanja memorijom. Kada računar alocira memoriju za dinamičke strukture poput listi, jasno se vidi kako softverski zadaci koriste fizičke resurse računara.

Praćenje memorije takođe omogućava razumijevanje kako moderni programski zadaci, napisani u apstraktnom jeziku visokog nivoa, postaju konkretne operacije koje upravljaju memorijom. Na taj način, liste nisu samo apstraktne strukture podataka, već i alat za povezivanje softverske logike s fizičkom memorijom računara. Dodavanje elemenata u listu ili manipulacija podacima direktno se reflektuje na memorijsko opterećenje, što se može pratiti pomoću sistemskih alata na svakom operativnom sistemu.

## Sažetak

Liste u Pythonu pružaju moćan i fleksibilan alat za organizaciju podataka, omogućavajući skladištenje elemenata različitih tipova i dinamičko upravljanje njihovom veličinom. Njihova ključna prednost je mogućnost grupisanja više podataka unutar jedne strukture, čime se pojednostavljuje rad s kompleksnim skupovima informacija.

Osnovne tehnike rada sa listama uključuju kreiranje pomoću uglastih zagrada, generisanje praznih ili unaprijed popunjenih listi te spajanje više listi u novu. Kroz ove operacije, liste se prilagođavaju različitim potrebama, od inicijalnog definisanja podataka do dinamičkog generisanja njihovog sadržaja.

Pristup elementima omogućen je putem indeksa, s pozitivnim i negativnim vrijednostima koje pružaju fleksibilnost u radu s podacima. Uz to, modifikacija elemenata, zamjena dijelova liste i brisanje pojedinačnih ili više elemenata pružaju kontrolu nad sadržajem liste, čineći ih prilagodljivim za različite zadatke.

Iteracija kroz liste predstavlja osnovnu tehniku za obradu podataka, omogućavajući analizu, filtriranje i transformaciju elemenata. Korištenjem petlji i logičkih uslova, moguće je izdvojiti specifične elemente, prilagoditi njihove vrijednosti ili generisati nove liste na osnovu postojećih. Kombinacijom iteracije i kumulativnih operacija, poput sumiranja i množenja, liste se koriste za implementaciju naprednijih programskih rješenja.

Ugniježdene liste omogućavaju rad s višedimenzionalnim strukturama, poput matrica i tabela. Dinamičko kreiranje i modifikacija ugniježdenih listi pružaju alate za organizaciju podataka složene hijerarhije, dok višestruki nivoi ugniježdenosti omogućavaju detaljnu analizu i manipulaciju.

Kopiranje listi je važno za stvaranje nezavisnih verzija podataka, čime se izbjegavaju neželjene promjene na originalnim strukturama. Plitko kopiranje omogućava kopiranje prvog nivoa liste, dok duboko kopiranje stvara potpuno nezavisne kopije ugniježdenih struktura.

Sintaksa za stvaranje listi (list comprehension) predstavlja elegantan način za generisanje novih listi primjenom izraza i uslova. Ova tehnika pojednostavljuje kod i omogućava istovremenu transformaciju i filtriranje elemenata, što je posebno korisno za obradu velikih skupova podataka.

Liste nisu samo alat za organizaciju podataka već i odraz načina na koji Python upravlja memorijom. Dinamička alokacija memorije i fleksibilnost u radu s elementima pokazuju kako liste efikasno koriste resurse računara, omogućavajući njihovu primjenu u širokom spektru programskih rješenja.