Rešitev (v enem kosu)

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!

Zadnja sprememba: sreda, 22. januar 2014, 18.19