Punce so naročale malico. Naročila shranjujemo v seznamu terk, kot na primer

narocila = [("Ana", "sendvič"), ("Berta", "sendvič"), ("Cilka", "burek"),
            ("Dani", "jogurt"), ("Ema", "sendvič"), ("Fanči", "burek")]

Za opravljeno domačo nalogo morate oddati vsaj ogrevalni in obvezni del.

Ogrevalna naloga

Napiši funkcijo stevilo_narocil(narocila, hrana), ki prejme seznam, kakršen je zgornji, in ime neke hrane, vrne pa število ljudi, ki so naročili to hrano. Če je narocila gornji seznam bo klic stevilo_narocil(narocila, "sendvič") vrnil 3, klic stevilo_narocil(narocila, "korenček") pa 0.

Napiši funkcijo goljufi(narocila), ki vrne abecedno urejen seznam imen vseh, ki so naročili več kot eno malico. V gornjem primeru takšnih ni. Če pokličemo goljufi([("Ana", "burek"), ("Berta", "sendvič"), ("Berta", "sendvič"), ("Cilka", "sendvič"), ("Berta", "korenček"), ("Ana", "burek")]), pa vrne seznam ["Ana", "Berta"]. Ana je na seznamu, ker je naročila dvakrat - pa čeprav isto jed.

Napiši funkcijo narocniki(narocila, hrana), ki vrne abecedno urejen seznam imen vseh, ki so naročili podano hrano. Če so narocila seznam z začetka besedila, klic narocniki(narocila, "sendvič") vrne ["Ana", "Berta", "Ema"], klic narocniki(narocila, "korenček") pa [].

Rešitev

Pri ogrevalnih nalogah slovarjev sploh nismo potrebovali.

Da izvemo, kolikokrat je bila nekaj jed naročena, pač gremo čez seznam in štejemo. Ker je narocila seznam parov imen oseb in narocil, je prek njega najprimerneje iti z for oseba, naroclio in narocila. V resnici pa imen niti ne potrebujemo in spremenljivke, ki jih ne potrebujemo, najraje poimenujemo _. Torej

def stevilo_narocil(narocila, hrana):
    stevec = 0
    for _, narocilo in narocila:
        if narocilo == hrana:
            stevec += 1
    return stevec

Da poiščemo goljufe, gremo prav tako čez seznam, le da nas zdaj zanimajo osebe, ne naročila. Vzdržujemo seznam tistih, ki so že naročili, in seznam goljufov. Če nekoga, ki nekaj naroča, zalotimo v seznamu tistih, ki so že naročili, ga dodamo na seznam goljufov - razen, če je že tam.

def goljufi(narocila):
    ze_narocili = []
    seznam_goljufov = []
    for oseba, _ in narocila:
        if oseba in ze_narocili:
            if oseba not in seznam_goljufov:
                seznam_goljufov.append(oseba)
        else:
            ze_narocili.append(oseba)
    return sorted(seznam_goljufov)

Funkcija narocniki pa je precej podobna funkciji stevilo_narocil, le da namesto, da bi povečevali števec, dodajamo naročnike v seznam. Na koncu vrnemo urejen seznam.

def narocniki(narocila, hrana):
    seznam_narocnikov = []
    for oseba, narocilo in narocila:
        if narocilo == hrana:
            seznam_narocnikov.append(oseba)
    return sorted(seznam_narocnikov)

V resnici bi lahko najprej sprogramirali funkcijo narocniki, funkcijo stevilo_narocil pa nagoljufali kar z

def stevilo_narocil(narocila, hrana):
    return len(narocniki(narocila, hrana))

Obvezna naloga

Napiši funkcijo slovar_narocil(narocila), ki prejme seznam, kakršen je gornji, vrne pa slovar, katerega ključi so imena oseb, pripadajoče vrednosti pa hrana, ki so jo naročile. Za gornja naročila funkcija vrne {"Ana": "sendvič", "Berta": "sendvič", "Cilka": "burek", "Dani": "jogurt", "Ema": "sendvič", "Fanči": "burek"}.

Napiši funkcijo kosovnica(narocila), ki prejme seznam naročil in vrne slovar, katerega ključi so različne malice, vrednosti pa število oseb, ki so naročile to malico. Za gornja naročila funkcija vrne {"sendvič": 3, "burek": 2, "jogurt": 1}.

Napiši funkcijo kosovnica_brez_goljufov(narocila), ki dela podobno kot prejšnja funkcija, le da pri vsaki osebi upošteva le njeno prvo naročilo.

Napiši funkcijo najpopularnejse(narocila), ki vrne ime največkrat naročene jedi. Predpostavljajmo, da je le ena. Za gornja naročila funkcija vrne "sendvič".

Napiši funkcijo primanjkljaj(kosovnica, zaloga), ki prejme slovar, kakršnega vrača funkcija kosovnica in podoben slovar, katerega ključi so imena jedi, vrednosti pa število kosov te jedi, ki jih imamo na zalogi. Funkcija vrne slovar, ki pove, koliko kosov jedi nam manjka. Klic primanjkljaj({"sendvič": 3, "burek": 2, "jogurt": 1}, {"sendvič": 2, "jogurt": 2, "korenček": 4}) vrne, {"sendvič": 1, "burek": 2}.

Rešitev

Slovar vseh naročil lahko naredimo tako.

def slovar_narocil(narocila):
    slovar = {}
    for oseba, narocilo in narocila:
        slovar[oseba] = narocilo
    return slovar

Pri tem je potrebno opozoriti, da je slovar zelo slabo ime za spremenljivko, ki je slovar, saj pove, za kakšno vrsto (tip) spremenljivke gre, ne pa, kaj ta spremenljivka vsebuje. No, v tem primeru pa ima smisel, saj je bistvo te spremenljivke prav to, da je slovar.

Funkcijo slovar_narocil napišemo krajše. Vemo že, da int in float pretvarjata v števila, str v nize in list v sezname. Prav tako dict pretvarja v slovarje, kot argument pa zahteva seznam parov. Točno to, kar imamo. Zato lahko napišemo kar:

def slovar_narocil(narocila):
    return dict(narocila)

Za kosovnico bomo uporabili slovar s privzetimi vrednostmi. Te bodo tipa int.

def kosovnica(narocila):
    po_hrani = defaultdict(int)
    for _, narocilo in narocila:
        po_hrani[narocilo] += 1
    return po_hrani

Kosovnica brez goljufov bo podobna, le še seznam goljufov moramo vzdrževati, in jih ignorirati, ko oddajo drugo naročilo.

def kosovnica_brez_goljufov(narocila):
    ze_narocili = []
    po_hrani = defaultdict(int)
    for oseba, narocilo in narocila:
        if oseba not in ze_narocili:
            po_hrani[narocilo] += 1
            ze_narocili.append(oseba)
    return po_hrani

Za najpopularnejše naročilo gremo prek seznama in si zapomnimo najpopularnejšo hrano - v začetku je ta None, potem pa pri vsaki hrani, pogledamo, ali jo je naročilo več kot to. Če doslej nismo naročili še nobene, pa je trenutna (to je prva) hrana itak brez konkurence.

def najpopularnejse(narocila):
    kosov = kosovnica(narocila)
    naj_hrana = None
    for hrana, narocil in kosov.items():
        if naj_hrana == None or narocil > kosov[naj_hrana]:
            naj_hrana = hrana
    return naj_hrana

Funkcijo lahko napišemo še na kup načinov. Lahko, recimo, najprej izvemo popularnost najpopularnejše hrane, potem pa vrnemo prvo hrano s to popularnostjo.

def najpopularnejse(narocila):
    kosov = kosovnica(narocila)
    m = max(kosov.values())
    for hrana, narocil in kosov.items():
        if narocil == m:
            return hrana

Le kdor res pozna Python - bolj, kot ga bomo spoznali pri tem predmetu - pa razume in sme napisati

def najpopularnejse(narocila):
    kosov = kosovnica(narocila)
    return max(kosov, key=kosov.get)

Primanjkljaj bi si želeli izračunati z

def primanjkljaj(kosovnica, zaloga):
    manjka = {}
    for hrana, kosov in kosovnica.items():
        na_zalogi = zaloga[hrana]  # ne deluje!
        if na_zalogi < kosov:
            manjka[hrana] = kosov - na_zalogi
    return manjka

Kako to, deluje, je očitno. Očitno pa je tudi, da ne deluje: crkne v vrstici na_zalogi = zaloga[hrana]. Če hrana ni na zaloga (oprostite za pomanjkanje sklon v ta stavek), potem ta ključ ni v slovar in bomo fasali KeyError. Rešimo se tako, da pred to vrstico preverimo if hrana in zaloga in v nasprotnem primeru nastavimo na_zalogi = 0. Še lepše nas reši get, ki mu kot privzeto vrednost damo 0.

def primanjkljaj(kosovnica, zaloga):
    manjka = {}
    for hrana, kosov in kosovnica.items():
        na_zalogi = zaloga.get(hrana, 0)
        if na_zalogi < kosov:
            manjka[hrana] = kosov - na_zalogi
    return manjka

Dodatna naloga

Napiši funkcijo vsa_narocila(narocila), ki vrne slovar, katerega ključi so imena jedi, vrednosti pa seznami imen oseb, ki so naročili to jed. Vrstni red imen naj bo kar enak vrstnemu redu, v katerem se pojavljajo v seznamu naročil. Za gornja naročila funkcija vrne {"sendvič": ["Ana", "Berta", "Ema"], "burek": ["Cilka", "Fanči"], "jogurt": ["Dani"]}

Napiši funkcijo oddaj(narocila, zaloga), ki prejme seznam naročil (kot z začetka naloge) in slovar z zalogo (kot pri funkciji primanjkljaj). Funkcija oddaj ne vrne ničesar! Pač pa gre čez seznam naročil. Če naročilo lahko izpolni, ga pobriše iz seznama in zmanjša zalogo te jedi. Če ga ne more izpolniti, ker jedi ni (več) na zalogi, ga pusti v seznamu.

Recimo, da imamo

narocila = [("Ana", "sendvič"), ("Berta", "sendvič"), ("Cilka", "burek"),
            ("Dani", "jogurt"), ("Ema", "sendvič"), ("Fanči", "burek")]
zaloga = {"sendvič": 7, "jogurt": 1, "korenček": 1}

Če pokličemo oddaj(narocila, zaloga), se seznam narocila spremeni v [("Cilka", "burek"), ("Fanči", "burek")], slovar zaloga pa v{"sendvič": 4, "korenček": 1}.

Spomni se, da i-ti element seznama s pobrišemo z del s[i]. Vendar ... se bo reč potem še malo (in najbrž nepredvideno) zapletla.

Rešitev

Za slovar vseh naročil bomo uporabili defaultdict; ker morajo biti vrednosti slovarji, bo privzeta vrednost pač list.

Potem pa gremo čez vsa naročila in k seznamu, ki pripada ključu narocilo, dodamo osebo.

def vsa_narocila(narocila):
    po_hrani = defaultdict(list)
    for oseba, narocilo in narocila:
        po_hrani[narocilo].append(oseba)
    return po_hrani

Oddaja naročila pa je zoprna reč. V bistvu bi radi tole.

def oddaj(narocila, zaloga):
    for hrana, _ in narocila:
        if hrana in zaloga:
            zaloga[hrana] -= 1
            if not zaloga[hrana]:
                del zaloga[hrana]

Vendar ne deluje: nekatere elemente naročila preprosto preskoči. To se zgodi zato, ker zanka for jemlje ničti, prvi, drugi, tretji (... in tako naprej) element naročila. Če recimo, pobrišemo prvega, potem drugi postane prvi, zanka pa vseeno nadaljuje z drugim, ki pa je bil prej tretji. Drugi se izpodmakne, uide.

Pravilna rešitev (seveda ne edina, vendar kar običajna) je:

def oddaj(narocila, zaloga):
    i = 0
    while i < len(narocila):
        hrana = narocila[i][1]
        if hrana in zaloga:
            zaloga[hrana] -= 1
            if not zaloga[hrana]:
                del zaloga[hrana]
            del narocila[i]
        else:
            i += 1

Namesto for smo uporabili while, i pa predstavlja indeks v seznam. Indeks povečamo (i += 1) le, če naročila ne pobrišemo. Če ga pobrišemo, pa pustimo i pri miru, saj bo na i-to mesto prišel naslednji element.

Ta naloga ima zelo pomemben nauk: seznama (ali slovarja ali česa drugega) prek česar gremo z zanko, ne smemo spreminjati. Predvsem: ne smemo ga krajšati.

마지막 수정됨: 화요일, 24 2월 2026, 5:53 PM