Ideja za nalogo izvira iz Tedna programiranja, na katerem je Katja razmišljala o tem, kako sestaviti program za izračun zalog, ki jih je potrebno kupiti za tabor. Hvala. ;)

Testi

Testi: testi-jedilniki.py

Recepti

Slovar jedi pove, kaj potrebujemo za posamezno jed. Čeprav to ni preveč lepo, bo ta slovar kar globalna spremenljivka - funkcije je bodo videle, čeprav je ne bodo dobile kot argument.

Ključi slovarja so jedi. Vrednosti so slovarji, ki vsebujejo sestavine in količino te sestavine, ki je potrebna za en obrok (eno osebo). Tako za palačinke za enega človeka potrebujemo eno jajce, 0,3 mleka in 0,2 moke. (Enot ne omenjam, ker so številke itak izmišljene; recimo, da potrebujemo 0,3 milipugne mleka in 0,2 bante moke.

jedi = { "palacinke": {"jajce": 1, "mleko": 0.3, "moka": 0.2}, "smorn": {"jajce": 1, "mleko": 0.3, "moka": 0.2}, "krompirjev golaz": {"krompir": 2, "paprika": 1, "cebula": 1}, "sataras": {"paradiznik": 1, "paprika": 1, "jajce": 1, "cebula": 1, "kruh": 0.2}, "hrenovke": {"hrenovka": 1, "kruh": 0.2}, "makaroni": {"makaroni": 0.2, "paradiznik": 0.5, "meso": 0.2, "cebula": 0.3}, "marmelada": {"marmelada": 0.1, "kruh": 0.3}, "piškot": {"piškot": 2} }

Ogrevalna naloga

Napišite funkcijo ena_jed(jed, jedcev), ki kot argument dobi jed, ki jo želimo pripraviti in koliko obrokov. Kot rezultat vrne slovar s potrebnimi količinami.

>>> ena_jed("smorn", 4) {'moka': 0.8, 'mleko': 1.2, 'jajce': 4}

Rešitev

Tule so si nekateri polomili zobe, ko so počeli nekaj v tem slogu:

def ena_jed(jed, jedcev): seznam = jedi[jed] for sestavina in seznam: seznam[sestavina] *= jedcev return seznam

To ne gre, ker tako napisana funcija v resnici spreminja recept. Ime seznam se po tem nanaša na slovar s sestavinami za, recimo, palačinke - ne na nek nov slovar, temveč na prav onega, ki je podan v programu. S tem, ko povečate količine v njem, bodo "ostale" povečane tudi za kasneje.

Nekateri so to reševali tako, da so skopirali slovar - kar ni nujno povsem preprosto. Veliko lažje je pisati v nov slovar.

def ena_jed(jed, jedcev): sestavine = {} for sestavina, kolicina in jedi[jed].items(): sestavine[sestavina] = kolicina * jedcev return sestavine

Kar zdaj povejmo tudi, kar se bomo naučili čez dva tedna.

def ena_jed(jed, jedcev): return {sestavina: kolicina * jedcev for sestavina, kolicina in jedi[jed].items()}

Obvezna naloga

Napišite funkcijo nakup(obroki), ki kot argument dobi seznam jedi in količin obrokov, kot rezultat vrne vse, kar je potrebno nakupiti.

>>> obroki = [("makaroni", 20), ("krompirjev golaz", 25), ("hrenovke", 18), ("sataras", 18)] >>> nakup(obroki) {'meso': 4.0, 'cebula': 49.0, 'kruh': 7.2, 'jajce': 18.0, 'hrenovka': 18.0, 'paprika': 43.0, 'paradiznik': 28.0, 'makaroni': 4.0, 'krompir': 50.0}

Poleg tega napišite funkcijo obrokov(jed, zaloga), ki dobi ime jedi, ki jo želimo pripraviti in zaloge, ki so na voljo. Zaloga je spet opisana kot slovar, katerega ključi so sestavine in vrednosti količina sestavine, ki jo imamo. Funkcija mora vrniti število obrokov, ki jih je možno pripraviti.

zaloga = {"jajca": 10, "mleko": 2, "moka": 2, "marmelada": 2, "kruh": 1} >>> obrokov("palacinke", zaloga) 6.0 >>> obrokov("marmelada", zaloga) 3.0 >>> obrokov("makaroni", zaloga) 0.0

Zakaj šest palačink? Imamo deset jajc in za en obrok palačink potrebujemo eno jajce, torej bi lahko naredili deset palačink. Žal pa imamo le 2 (litra?) mleka in za obrok palačink potrebujemo 3 dl(?). Torej je mleka dovolj za 6 palačink (Prav, za šest in dve tretjini, vendar hočemo, da so vsi obroki celi. Torej šest.) Moke je dovolj za deset obrokov. Skratka, materiala je za šest obrokov palačink, zaradi mleka.

Marmelade (očitno je s tem mišljen kruh z marmelado) imamo za tri obroke, zaradi pomanjkanja kruha.

Makaronov ... nimamo.

Rešitev

Sestavimo slovar, v katerega bomo seštevali vse, kar je potrebno nakupiti. Nato gremo čez vse obroke in za vsakega čez vse sestavine ter ju prištevamo v slovar.

def nakup(obroki): sestavine = collections.defaultdict(float) for jed, jedcev in obroki: for sestavina, kolicina in ena_jed(jed, jedcev).items(): sestavine[sestavina] += kolicina return sestavine

Nekateri ste si želeli seštevanja slovarjev. To žal ne gre. Slovarji niso mišljeni za take reči.

Da ugotovimo, koliko neke jedi bomo lahko pripravili, gremo prek vseh njenih sestavin in preverimo, za koliko ljudi je bo. To izračunamo tako, da zalogo posamezne reči celoštevilsko delimo s potrebno količino na osebo. Zapomnimo si najmanjšo številko.

def obrokov(jed, zaloga): koliko = None for sestavina, kolicina in jedi[jed].items(): imamo = zaloga.get(sestavina, 0) // kolicina if koliko is None or imamo < koliko: koliko = imamo return koliko

Ne spreglejte, da smo količino, ki jo imamo na zalogi, pridobili z zaloga.get(sestavina, 0) in ne zaloga[sestavina]. To je praktično, ker na ta način za reči, ki jih ni na zalogi, brez posebnega manevriranja izvemo, da je zaloga enaka 0. Brez tega bi morali pisati

if sestavina in zaloga: imamo = zaloga[sestavina] // kolicina else: imamo = 0

Če ne želimo sami iskati minimuma, lahko vse skupaj zlagamo v seznam in uporabimo funkcijo min.

def obrokov(jed, zaloga): koliko = [] for sestavina, kolicina in jedi[jed].items(): koliko.append(zaloga.get(sestavina, 0) // kolicina) return min(koliko)

To je lepo, ker privede do prej omenjene snovi za čez dva tedna. Takrat bomo znali uporabiti min, ne da bi morali za to sestaviti seznam.

def obrokov(jed, zaloga): return min(zaloga.get(sestavina, 0) // kolicina for sestavina, kolicina in jedi[jed].items())

Dodatna naloga

Napišite funkcijo, prazni(obroki, zaloga), ki kot argument prejme obroke (tako kot funkcija nakup) in zalogo (tako kot funkcija obrokov. Funkcija prazni naj zmanjša zalogo za toliko, kolikor bomo porabili za pripravo obrokov. Funkcija naj ne vrne ničesar.

Predpostaviti smete, da je za vse, kar želimo skuhati, dovolj zalog.

Rešitev

Zakaj se to imenuje "dodatna naloga", ne vem.

def prazni(obroki, zaloga): for sestavina, kolicina in nakup(obroki).items(): zaloga[sestavina] -= kolicina
Zadnja sprememba: torek, 23. marec 2021, 20.16