Izpit je bil sestavljen takole:

  1. Soda in liha: delo s seznami; spreminjanje seznama, podanega kot argument (imenski prostori...)
  2. Decimalno: rekurzija
  3. Ograje: seznami, množice, telovadba z zankami
  4. Tečaji: delo s slovarji in množicami, malo telovadbe s pogoji in zankami
  5. Blok: objektno programiranje, slovarji

Soda in liha

Pripravimo seznam lihih in seznam sodih števil. Nato gremo čeznju in jih paroma zlagamo v novi seznam. Če pri tem uporabimo zip, bo ta sam odrezal odvečne elemente.

Dodatna težava v nalogi je bila, da moramo spremeniti podani seznam, ne pa ustvariti novega. Seznamu torej ne bomo ničesar prirejali (v slogu s = nekaj), temveč ga bomo le spreminjali.

def soda_liha(s): soda = [x for x in s if x % 2 == 0] liha = [x for x in s if x % 2 == 1] s.clear() for so, li in zip(soda, liha): s += (so, li)

Naloga je nekoliko sitna. Diši po tem, da bi se jo moralo dati rešiti krajše, vendar je ta, krajša rešitev, grda. Vseeno jo objavimo, pa naj jo gledajo tisti, ki jim je takšna pornografija blizu.

from functools import reduce def soda_liha(s): s[:] = reduce(list.__iadd__, zip((x for x in s if x % 2 == 0), (x for x in s if x % 2 == 1)), [])

Decimalno

Če je niz dolg 1, torej, če je enak "0" ali "1", ga le pretvorim v število, pa sem. Sicer pa pretvorim v dvojiški zapis vse razen zadnje števke, to pomnožim z 2 in prištejem zadnjo števko.

def decimalno(s): if len(s) == 1: return int(s) return 2 * decimalno(s[:-1]) + int(s[-1])

Zvitejši naredijo tole:

def decimalno(s): return 2 * int(len(s) > 1 and decimalno(s[:-1])) + int(s[-1])

Za izziv pobirajmo števke od spredaj, ne od zadaj. Napišimo rešitev, brez komentarja.

def decimalno1(s): if len(s) == 1: return 2, int(s) m, i = decimalno1(s[1:]) return 2 * m, i + int(s[0]) * m def decimalno(s): return decimalno1(s)[1]

Zdi se mi, da bi se to, zadnje, moralo dati narediti tudi lepše, vendar mi ne pride nič pametnega na misel. Če kdo ve, naj pove.

Ograje

Naloga Ograje je vaja iz uporabe seznamov in množic (ter malo zank in pogojev). Poleg tega je, upam, pokazala, da se splača slediti objavljenim rešitvam domačih nalog. Je namreč silno funkciji ni_sosednjih, ki jo je bilo potrebno narediti pri domači nalogi Pike (tam je predstavljala sicer delček naloge za oceno 10 - vendar le preprostejši delček). Namig je namreč predlagal, da od vseh možnih ograj odštejemo tiste med polji z isto črko. Kdor je bral rešitve domačih nalog in se je spomnil na to nalogo, je lahko rešitev praktično prepisal, le da tam preverjamo, ali obstajajo sosedi z isto številko (no, v našem primeru črko), tule pa ne preverjamo obstoja temveč štejemo.

Razčistimo še, koliko bi bilo ograj, če bi imela vsa polja različne lastnike. Če je polje dimenzije m*n, potrebujem (n+1)*m navpičnih ograj in (m+1)*n vodoravnih, torej skupaj (n+1)*m + (m+1)*n. Lahko pa obrnemo drugače in vsakemu polju postavimo levo in zgornjo ograjo; teh je 2*m*n, poleg tega pa spodnjim poljem še spodnjo in desnim še desno, zato 2*m*n + m + n. Oboje je isto.

def ograje(s): ista = 0 for v in s: for i, j in zip(v, v[1:]): if i == j: ista += 1 for v1, v2 in zip(s, s[1:]): for i, j in zip(v1, v2): if i == j: ista += 1 return 2 * len(s) * len(s[0]) + len(s) + len(s[0]) - ista

Če vemo, da je True isto kot 1 in False isto kot 0, se rešitev še nekoliko poenostavi.

def ograje(s): ista = 0 for v in s: for i, j in zip(v, v[1:]): ista += i == j for v1, v2 in zip(s, s[1:]): for i, j in zip(v1, v2): ista += i == j return 2 * len(s) * len(s[0]) + len(s) + len(s[0]) - ista

To vodi v rešitev, ki le prešteje, kar je treba.

def sogovorniki(s): return 2 * len(s) * len(s[0]) + len(s) + len(s[0]) - \ sum(i == j for v in s for i, j in zip(v, v[1:])) - \ sum(i == j for v1, v2 in zip(s, s[1:]) for i, j in zip(v1, v2))

Tečaji

Tole je vaja iz dela s slovarji in množicami.

def opravil(ime, tecaj, tecaji): tecaji[ime].add(tecaj) def najbolj_ucen(tecaji): def deg(k): return len(tecaji[k]) return max(tecaji, key=deg) def vsi_tecaji(tecaji): vsi = set() for dst in tecaji.values(): vsi |= dst return vsi def neopravljeni(ime, tecaji): return vsi_tecaji(tecaji) - tecaji[ime]

Čim razumemo, kaj naj bi bilo v slovarju (in če smo že kdaj uporabljali slovarje s privzetimi vrednostmi), je opravil trivialna: le tečaj doda v ustrezno množico.

najbolj_ucen ponavlja ničkolikokrat izvedeni dril: iskanje največjega elementa. Gornja rešitev je še nekako v dosegu tega, kar smo počeli pri Programiranju 1 (čeprav uporabe argumenta key pri funkciji max res nismo posebej kazali. Kdor ne zna tako, pa je še vedn lahko naredil tako, kot smo sto in enkrat naredili na predavanjih, vajah in v domačih nalogah. Recimo tako:

def najbolj_ucen(tecaji): naj = None for kdo, komu in tecaji.items(): if naj is None or len(komu) > len(tecaji[naj]): naj = kdo return naj

Funkcija vsi_tecaji le zloži v isto množico vse vrednosti.

Neopravljeni tečaji pa so vsi, razen opravljenih.

V praksi bi resen programer te funkcije napisal takole:

def opravil(ime, tecaj, tecaji): tecaji[ime].add(tecaj) def najbolj_ucen(tecaji): return max(tecaji, key=lambda k: len(tecaji[k])) from functools import reduce def vsi_tecaji(tecaji): return reduce(set.union, tecaji.values(), set()) def neopravljeni(ime, tecaji): return vsi_tecaji(tecaji) - tecaji[ime]

Blok

Ko se enkrat odločimo, kaj bomo shranjevali v razredu Blok, so vse metode le začetniške vaje iz slovarjev. Metoda stanovalec bo morala vračati imena stanovalcev v določeni hiši, torej bo očitno potrebno shranjevati imena ljudi v slovarju, katerega ključi so hiše.

class Naselje: def __init__(self, hise): self.hise = dict.fromkeys(hise) def vseli(self, hisa, ime): if self.hise[hisa] is not None: return False self.hise[hisa] = ime return True def stanovalec(self, hisa): return self.hise[hisa] def izseli(self, hisa): self.hise[hisa] = None def prostih(self): p = 0 for ime in self.hise.values(): if ime is None: p += 1 return p

V konstruktorju smo uporabili droben trik: slovar, katerega kljui so elementi seznama hise, vrednosti pa None, smo sestavili z dict.fromkeys(hise). Če tega ne poznamo (in večina tega najbrž ne pozna), potrebujemo

def __init__(self, hise): self.hise = {hisa: None for hisa in hise}

ali, če imamo fobijo pred izpeljanimi seznami

def __init__(self, hise): self.hise = {} for hisa in hise: self.hise[hisa] = None

Vse skupaj

Celotna rešitev izpita, kot bi jo pričakovali od poprečnega študenta, ki se ni pretirano poglobil v izpeljane sezname in podobne trike, zna pa solidno programirati, je takšna.

# 1 def soda_liha(s): soda = [x for x in s if x % 2 == 0] liha = [x for x in s if x % 2 == 1] s.clear() for so, li in zip(soda, liha): s += (so, li) # 2 def decimalno(s): if len(s) == 1: return int(s) return 2 * decimalno(s[:-1]) + int(s[-1]) # 3 def ograje(s): ista = 0 for v in s: for i, j in zip(v, v[1:]): ista += i == j for v1, v2 in zip(s, s[1:]): for i, j in zip(v1, v2): ista += i == j return 2 * len(s) * len(s[0]) + len(s) + len(s[0]) - ista # 4 def opravil(ime, tecaj, tecaji): tecaji[ime].add(tecaj) def najbolj_ucen(tecaji): def deg(k): return len(tecaji[k]) return max(tecaji, key=deg) def vsi_tecaji(tecaji): vsi = set() for dst in tecaji.values(): vsi |= dst return vsi def neopravljeni(ime, tecaji): return vsi_tecaji(tecaji) - tecaji[ime] # 5 class Naselje: def __init__(self, hise): self.hise = {hisa: None for hisa in hise) def vseli(self, hisa, ime): if self.hise[hisa] is not None: return False self.hise[hisa] = ime return True def stanovalec(self, hisa): return self.hise[hisa] def izseli(self, hisa): self.hise[hisa] = None def prostih(self): p = 0 for ime in self.hise.values(): if ime is None: p += 1 return p
Zadnja sprememba: torek, 3. februar 2015, 12.50