Tule so rešitve vseh izpitnih nalog v obliki, kakršno bi pričakovali.
Podrobna razlaga in različne krajše in daljše variante so opisane spodaj.
def loci(cebele):
sode = []
lihe = []
for ime, koliko in cebele:
if koliko % 2 == 0:
sode.append(ime)
else:
lihe.append(ime)
return sode, lihe
from collections import Counter
def skrij(beseda):
crk = Counter(beseda)
nova_beseda = ""
for crka, pojavitev in sorted(crk.items()):
nova_beseda += crka + str(pojavitev)
return nova_beseda
def srecanje(vrt):
cas = (len(vrt) + sum(vrt)) / 2
cvet = -1
while cas > 0:
cvet += 1
cas -= 1 + vrt[cvet]
if cas == 0:
cvet += 0.5
return cvet
def vnukov(ime, rodovnik):
return sum(len(rodovnik[otrok]) for otrok in rodovnik[ime])
def najvec_vnukov(ime, rodovnik):
return max([vnukov(ime, rodovnik)] +
[najvec_vnukov(otrok, rodovnik) for otrok in rodovnik[ime]])
from math import pi
class Liki:
def __init__(self):
self.s_krogi = []
self.s_pravokotniki = []
def krog(self, x, y, r):
self.s_krogi.append((x, y, r))
def pravokotnik(self, x1, y1, x2, y2):
self.s_pravokotniki.append((x1, y1, x2, y2))
def ploscina(self):
return sum(pi * r ** 2 for _, _, r in self.s_krogi) + \
sum(abs(x1 - x2) * abs(y1 - y2) for x1, y1, x2, y2 in self.s_pravokotniki)
def krogi(self, max_r):
return [(x, y) for x, y, r in self.s_krogi if r < max_r]
Komentarji rešitev
Sodost čebel
Gornje rešitve ni kaj komentirati: gremo čez seznam in vržemo vsako čebelo
v seznam, v katerega sodi. Vedeti moramo le še, kako vrnemo dva seznama
naenkrat - če je tu kaj posebnega vedeti, seveda.
Paziti je potrebno le, da ne pišemo lihe = sode = [], saj bosta
lihe in sode potem isti seznam.
def loci(cebele):
sode = []
lihe = []
for ime, koliko in cebele:
if koliko % 2 == 0:
sode.append(ime)
else:
lihe.append(ime)
return sode, lihe
Vsebino zanke lahko tudi nekoliko skrajšamo, tako da z if-else določamo,
v kateri seznam bomo dodajali.
def loci(cebele):
sode = []
lihe = []
for ime, koliko in cebele:
(sode if koliko % 2 == 0 else lihe).append(ime)
return sode, lihe
Pravzaprav pa tudi if-elsa ne potrebujemo:
def loci(cebele):
sode = []
lihe = []
for ime, koliko in cebele:
[sode, lihe][koliko % 2].append(ime)
return sode, lihe
Pa tudi dveh imen za seznama ne.
def loci(cebele):
sode_lihe = ([], [])
for ime, koliko in cebele:
sode_lihe[koliko % 2].append(ime)
return sode_lihe
Tole je bilo tako lepo, da nas rešitev z izpeljanimi seznami niti ne
impresionira preveč:
def loci(cebele):
sode = [ime for ime, koliko in cebele if koliko % 2 == 0]
lihe = [ime for ime, koliko in cebele if koliko % 2 == 1]
return sode, lihe
Rešitev v eni vrstici je pa sploh en grd dolgčas.
def loci(cebele):
return [ime for ime, koliko in cebele if koliko % 2 == 0], \
[ime for ime, koliko in cebele if koliko % 2 == 1]
Popravek: obstaja tudi lepa rešitev v eni vrstici. Napisal jo je eden od
vaših kolegov. Sram me je, da se je nisem spomnil sam, saj prejšnja rešitev vendar
kar kliče po tej:
def loci(cebele):
return tuple([i for i, s in cebele if s % 2 == k] for k in (0, 1))
Besedna igra
Pri besedni igri nam pride zelo prav razred Counter, ki smo ga
bežno omenili (če ne drugje, vsaj v zapiskih). Brez njega moramo črke
prešteti sami. Da se ne mučimo preveč, pa predpostavimo, da vemo vsaj za
defaultdict. Navsezadnje smo tudi v nalogi namignili, da utegne
priti prav.
from collections import defaultdict
def skrij(beseda):
crk = defaultdict(int)
for c in beseda:
crk[c] += 1
nova_beseda = ""
for crka, pojavitev in sorted(crk.items()):
nova_beseda += crka + str(pojavitev)
return nova_beseda
V prvi zanki preštejemo, kolikokrat se pojavi katera črka, v drugi sestavimo
novo besedo. Iz slovarja bomo vzeli pare crk.items(), ki bodo
sestavljeni iz terk (črka, število pojavitev). Novo besedo sestavimo tako, da
lepimo skupaj število pojavitev, ki jih spremenimo v niz
(str(pojavitev)) in črke.
Count, na katerega smo prav tako namignili v besedilu naloge,
nas reši preštevanja.
from collections import Counter
def skrij(beseda):
crk = Counter(beseda)
nova_beseda = ""
for crka in sorted(crk):
nova_beseda += crka + str(crk[crka])
return nova_beseda
Metoda join pa nas lahko reši (na nek način) zanke.
from collections import Counter
def skrij(beseda):
return "".join(crka + str(pojavitev) for crka, pojavitev in sorted(Counter(beseda).items()))
Čebele z leve in desne
Ob čebeli skupaj bosta potrebovali toliko časa, kolikor je cvetov (ker
potrebujeta sekundo za vsak cvet) in kolikor je nektarja (ker porabita sekundo
za vsako enoto). Obe skupaj bosta torej potrebovali
len(vrt) + sum(vrt) sekund. Če za eno čebelo ugotovimo, kje bo, ko
mine pol tega časa, vemo, kje se bosta srečali.
def srecanje(vrt):
cas = (len(vrt) + sum(vrt)) / 2
cvet = -1
while cas > 0:
cvet += 1
cas -= 1 + vrt[cvet]
if cas == 0:
cvet += 0.5
return cvet
Lahko bi začeli tudi s cvet = 0, vendar bi bila potem čebela
na koncu za en cvet predaleč (razmislite!). Popaziti moramo le na možnost, da
je čebela na polovici časa ravno opravila z enim od cvetov. V tem primeru bo
odletela naprej - druga pa z druge strani. Srečali se bosta na sredi, zato
prištejemo 0.5.
Največ vnukov
Najprej se pozabavajmo s funkcijo, ki pove, koliko vnukov ima posamezna
oseba. Iti moremo prek vseh otrok in seštevati število njihovih otrok.
def vnukov(ime, rodovnik):
vnukov = 0
for otrok in rodovnik[ime]:
vnukov += len(rodovnik[otrok])
return vnukov
Ali, krajše,
def vnukov(ime, rodovnik):
return sum(len(rodovnik[otrok]) for otrok in rodovnik[ime])
Zdaj pa gre po znanem vzorcu. Če hočemo odkriti največje število vnukov v
rodbini določene osebe, si najprej zapomnimo kar število vnukov te osebe. Nato
preverimo rodbine njenih otrok in če v kateri naletimo na večje število vnukov,
si zapomnimo le-to.
def najvec_vnukov(ime, rodovnik):
najvec = vnukov(ime, rodovnik)
for otrok in rodovnik[ime]:
otrokovih = najvec_vnukov(otrok, rodovnik)
if otrokovih > najvec:
najvec = otrokovih
return najvec
Ali, spet, krajše:
def najvec_vnukov(ime, rodovnik):
return max([vnukov(ime, rodovnik)] +
[najvec_vnukov(otrok, rodovnik) for otrok in rodovnik[ime]])
Liki
Objekt, self, bo očitno potreboval seznam pravokotnikov in
seznam krogov. Imenovali ju bomo s_krogi in
s_pravokotniki. Skušnjavi, da bi ju imenovali krogi
in pravokotniki, se moramo upreti, ker bo tako ime metodama; kaj
se zgodi, če damo metodi enako ime kot atributu, pa smo videli, ko smo se na
zadnjih predavanjih pogovarjali o rešitvi domače naloge.
Metode pa niso nič posebnega, sploh če nismo pozabili, kar smo se naučili
o izpeljanih seznamih in generatorjih.
Spodobi pa se, da pokažemo res lepo objektno rešitev, ki jo je napisal eden
vaših kolegov (ne isti kot zgoraj ;). Takšnih rešitev nismo pričakovali, ampak če bi
se zares učili o objektnem programiranju, bi delali tako:
class Liki:
class Krog:
def __init__(self, x, y, r):
self.s = (x, y)
self.r = r
def ploscina(self):
return math.pi*self.r*self.r
def rad(self, m):
return self.r < m
class Pravokotnik:
def __init__(self, x1, y1, x2, y2):
self.t1 = (x1, y1)
self.t2 = (x2, y2)
def ploscina(self):
return abs((self.t1[0]-self.t2[0])*(self.t1[1]-self.t2[1]))
def rad(self, m):
return False
def __init__(self):
self.liki = []
def krog(self, x, y, r):
self.liki.append(self.Krog(x, y, r))
def pravokotnik(self, x1, y1, x2, y2):
self.liki.append(self.Pravokotnik(x1, y1, x2, y2))
def ploscina(self):
return sum(i.ploscina() for i in self.liki)
def krogi(self, max_r):
return [i.s for i in self.liki if i.rad(max_r)]
Glejte predvsem, kako je narejeno računanje ploščine. To je to, to so objekti!