Barvanje
Testi
Ogrevanje
Šahovnica ima 64 polj. Stolpci so označeni s črkami A, B, C ..., H; vrstice so označene s številkami 1, 2, 3, ... 8. Oznake polj so torej, A5, C3 in podobno. Polja so navadno črna in bela. Tule pa predpostavimo, da imamo belo šahovnico. Pobarvamo jo tako, da na polja lepimo nalepke različnih barv. Če jih, na primer, lepimo takole:
[("A3", "zelena"), ("B5", "modra"), ("G2", "rumena")]
bo šahovnica pobarvana takole:
{"A3": "zelena", "B5": "modra", "G2": "rumena"}
Če pa bi jih, recimo, lepili tako:
[("A3", "zelena"), ("B5", "modra"), ("A3", "rumena")]
bi bila šahovnica pobarvana tako:
{"A3": "rumena", "B5": "modra"}
Polje A3 je rumeno, ker je rumena nalepka prekrila modro.
Napiši funkcijo
barvanje(barve)
, ki kot argument dobi seznam parov (koordinata, barva), in kot rezultat vrne barve polj v obliki slovarja, katerega ključi so koordinate in vrednosti barve - kot kaže gornji primer.Napiši funkcijo
barvanje_eno(barve)
, ki je podobna gornji, vendar ne lepi nalepk na polja, ki so že polepljena. To pomeni, da morabarvanje_eno([("A3", "zelena"), ("B5", "modra"), ("A3", "rumena")])
vrniti{"A3": "zelena", "B5": "modra"}
in ne{"A3": "rumena", "B5": "modra"}
.
Rešitev
Tako kot smo morali v funkcijah, ki naj bi vračale nek (nov) seznam, najprej narediti nov seznam,
bomo v tej, ki mora vračati slovar, najprej naredili slovar; sahovnica = {}
. Nato se sprehodimo
čez seznam parov (for polje, barva in barve:
) in za vsako polje zabeležimo njegovo barvo,
sahovnica[polje] = barva
. Tako dobimo
def barvanje(barve):
sahovnica = {}
for polje, barva in barve:
sahovnica[polje] = barva
return sahovnica
Kasnejša barva bo prekrila zgodnejšo preprosto zato, ker bomo istemu polju priredili novo vrednost.
Če tega nočemo, je pač ne priredimo, kadar je polje že pobarvano: v funkcijo vstavimo še en
if polje not in sahovnica
.
def barvanje_eno(barve):
sahovnica = {}
for polje, barva in barve:
if polje not in sahovnica:
sahovnica[polje] = barva
return sahovnica
Obvezne naloge
Recimo, da imamo takole polepljeno šahovnico:
sahovnica = {
"A3": "rumena", "A6": "rumena", "H2": "rumena", "G7": "rumena",
"C2": "zelena", "C3": "zelena",
"H6": "modra"
}
Napiši funkcijo
prestej_barvo(sahovnica, barva)
, ki kot argument dobi slovar, kakršen je gornji, in barvo, kot rezultat pa vrne število polj te barve. Tako mora, recimoprestej_barvo(sahovnica, "rumena")
v gornjem primeru vrniti 4, saj imamo štiri rumena polja.Napiši funkcijo
pobarvanih_polj(sahovnica)
, ki vrne število pobarvanih polj.Napiši funkcijo
nepobarvanih_polj(sahovnica)
, ki vrne število nepobarvanih polj.Napiši funkcijo
polja_po_barvi(sahovnica, barva)
, ki prejme šahovnico in barvo, kot rezultat pa vrne koordinate polj s to barvo. Tako mora pri gornji šahovnici klicpolja_po_barvi(sahovnica, "modra")
vrniti množico{"C2", "C3"}
. Klicpolj_po_barvi(sahovnica, "rjava")
mora v gornjem primeru vrniti prazno množico.
Rešitev
V prvi nalogi lahko gremo, recimo, čez vse vrednosti v šahovnici (sahovnica.values()
) in
štejemo, kolikokrat naletimo na iskano barvo.
def prestej_barvo(sahovnica, barva):
stevec = 0
for barva1 in sahovnica.values():
if barva1 == barva:
stevec += 1
return stevec
Lahko pa bi spremenili vrednosti v slovarju v seznam (list(sahovnica.values())
) in prepustili
štetje seznamovi metodi count()
.
def prestej_barvo(sahovnica, barva):
return list(sahovnica.values()).count(barva)
Če bi imeli že napisano funkcijo polja_po_barvi
, bi si lahko pomagali z njo in sestavili množico
vseh polj te barve ter vrnili velikost te množice.
def prestej_barvo(sahovnica, barva):
return len(polja_po_barvi(sahovnica, barva))
Če pa bi v okviru dodatne naloge že sprogramirali prestej_barve
, bi lahko prešteli kar vse barve
in vrnili število polj te barve.
def prestej_barvo(sahovnica, barva):
return prestej_barve(sahovnica)[barva]
Ups, ne, to ne deluje, če določena barva ne obstaja. Takole bo boljše: če barve ni, vrnimo 0.
def prestej_barvo(sahovnica, barva):
return prestej_barve(sahovnica).get(barva, 0)
Število pobarvanih polj je preprosto velikost slovarja. Število nepobarvanih pa je 64 - toliko.
def pobarvanih_polj(sahovnica):
return len(sahovnica)
def nepobarvanih_polj(sahovnica):
return 64 - len(sahovnica)
Ob tej nalogi ste bili fascinantno kreativni. Tipična rešitev je bila
def pobarvanih_polj(sahovnica):
pobarvanih = 0
for polje, barva in sahovnica.items():
pobarvanih += 1
return pobarvanih
Bilo pa je tudi veliko še bolj zapletenih.
Končno, za polja po barvi si pripravimo prazno množico (te_barve = set()
). Gremo čez šahovnico
(for polje, barva1 in sahovnica.items()
) in ko naletimo na polje iskane barva
(if barva == barva1
), dodamo koordinate v to množico (te_barva.add(polje)
).
def polja_po_barvi(sahovnica, barva):
te_barve = set()
for polje, barva1 in sahovnica.items():
if barva == barva1:
te_barve.add(polje)
return te_barve
Dodatna naloga
Napiši funkcijo
prestej_barve(sahovnica)
, ki vrne slovar, katerega ključi so barve, vrednosti pa število polj te barve. Pri gornji šahovnici mora funkcija vrniti slovar{"rumena": 4, "zelena": 2, "modra": 1}
.Napiši funkcijo
polja_po_barvah(sahovnica)
, ki vrne podoben slovar, le da so elementi množice s koordinatami polj te barve. V gornjem primeru bi morala funkcija vrniti slovar{"rumena": {"A3", "A6", "H2", "G7"}, "zelena": {"C2", "C3"}, "modra": {"H6"}}
Rešitev
Za dodatno nalogo mi prideta prav dve stvari, ki sem ju hvalil na predavanjih. Obe sta iz
modula collections
in se imenujeta Counter
in defaultdict
.
Za preštej barve uporabim Counter
. Spustim ga čez vrednosti (sahovnica.values()
), pa bo
preštel, kar je treba.
def prestej_barve(sahovnica):
return collections.Counter(sahovnica.values())
Za množice polj določene barve pa bom imel slovar po_barvah
, katerega ključi bodo barve in
vrednosti množice koordinat polj. Grem čez vsa polja in barve
(for polje, barva in sahovnica.items()
) in v slovar pod ključ barva
dodam polje
(po_barvah[barva].add(polje)
). Pri tem predpostavljam, da po_barvah[barva]
že vsebuje
množico. Pri barvah, ki jih vidim prvič, to ni res. Da mi ne bi bilo tega primera obravnavati
posebej (if barva not in po_barvah: po_barvah[barva] = set()
), po_barvah
ne bo običajen
slovar temveč slovar s privzetimi vrednostmi (po_barvah = collections.defaultdict(set)
).
def polja_po_barvah(sahovnica):
po_barvah = collections.defaultdict(set)
for polje, barva in sahovnica.items():
po_barvah[barva].add(polje)
return po_barvah
Hitrejša rešitev
Vsaj eden izmed vaših kolegov, ki lahko ostane anonimen, je napisal prav Metodične rešitve. Kar je tule spodaj naj berejo le pogumni: tole je narejeno toliko dobro, kot znam jaz, na enem mestu celo malo boljše in na enem morda malenkost nerodnejše.
import itertools
def barvanje(barve):
return dict(barve)
def barvanje_eno(barve):
return dict(reversed(barve))
def prestej_barvo(sahovnica, barva):
return list(sahovnica.values()).count(barva)
def pobarvanih_polj(sahovnica):
return len(sahovnica)
def nepobarvanih_polj(sahovnica):
return 64 - pobarvanih_polj(sahovnica)
def polja_po_barvi(sahovnica, barva):
return polja_po_barvah(sahovnica).get(barva, set())
def pari_po_barvah(sahovnica):
return itertools.groupby(sorted(sahovnica.items(), key=lambda par: par[1]), key=lambda par: par[1])
def prestej_barve(sahovnica):
return {barva: len(list(polja)) for barva, polja in pari_po_barvah(sahovnica)}
def polja_po_barvah(sahovnica):
return {barva: set(map(lambda par: par[0], polja)) for barva, polja in pari_po_barvah(sahovnica)}
V barvanje
je le pretvoril seznam parov v množico. Da, dict
zna tudi to. V barvanje_eno
se je
spomnil preprostega trika, ki tudi meni ni prišel na misel: isto kot barvanje, le seznam barv
obrne. Super ideja.
Naslednje tri funkcije so takšne, kot v "uradni" rešitvi. polja_po_barvi
uporablja
polja_po_barvah
, le z uporabo get
popazi, da v primeru, da določene barve ni, vrne prazno
množico.
Ostanek uporablja hujšo eksotiko. Pomožna funkcija pari_po_barvah
pogrupira polja po barvah in
zadnji dve funkciji uporabita te skupine.
Super. Dve stvari bi se dalo napisati malenkost preprosteje. Namesto lambda x: x[1] lahko uporabimo
funkcijo itemgetter(1)
, ki jo dobimo v modulu operator
.
from operator import itemgetter
def pari_po_barvah(sahovnica):
return itertools.groupby(sorted(sahovnica.items(), key=itemgetter(1)), key=itemgetter(1))
V polja_po_barvah
pa lahko uporabimo izpeljane množice (set comprehension).
def polja_po_barvah(sahovnica):
return {barva: {par[0] for par in polja} for barva, polja in pari_po_barvah(sahovnica)}