Testi

Testi: testi-o-kuharju-ki-ni-znal-steti.py

Naloga

V nekem filmi, katerega naslova se ne spomnim (smrdi pa mi po Johnu Cleesu) so imeli restavracijo, v kateri je znal šteti samo natakar. Če so gostje za mizo naročili, naročili pivo, tri sokove in dve kavi, je zatulil proti kuhinji "Pivo, sok, sok, sok, kava, kava".

Ogrevalna naloga

Napiši funkcijo, ki kot argument dobi seznam, kot je, recimo, ["pivo", (3, "sok"), (2, "kava")] in izpiše

pivo sok sok sok kava kava

Torej, v seznamu s so nizi in terke. Ko naletimo na niz, ga le izpišemo. Ko naletimo na terko, bo ta vedno vsebovala število in niz. Niz moramo izpisati tolikokrat, kolikor pravi število.

Namig: če imamo nek objekt e in bi radi preverili, ali gre za terko, to naredimo z

if isinstance(e, tuple): Ali, obratno, če želimo preveriti, ali gre za niz, rečemo if isinstance(e, str):

Obvezna naloga

Napiši funkciji expanded(s) in expand(s). Funkcija expanded naj prejme takšen seznam, kot ga opisuje ogrevalna naloga, in kot rezultat vrne nov seznam, v katerem je vse, kar je potrebno pripraviti za to mizo.

>>> s = ["pivo", (3, "sok"), (2, "kava")] >>> expanded(s) ['pivo', 'sok', 'sok', 'sok', 'kava', 'kava'] >>> print(s) ['pivo', (3, 'sok'), (2, 'kava')]

Funkcija expand(s) ne vrne ničesar, temveč spreminja podani seznam.

>>> s = ["pivo", (3, "sok"), (2, "kava")] >>> expand(s) >>> print(s) ['pivo', 'sok', 'sok', 'sok', 'kava', 'kava']

Dodatna naloga

Napiši funkciji imploded(s) in implode(s), ki delata ravno nasprotno od expanded(s) in expand(s).

Rešitev

Kot so pravilno ugotovili nekateri ... je bila ta domača naloga nekam lahka. ;)

Ogrevalna naloga

V ogrevalni nalogi ste, predvsem, preskusili isinstance.

def izpisi(s): for e in s: if isinstance(e, tuple): for i in range(e[0]): print(e[1]) else: print(e)

Expand(ed)

Skoraj enako kot izpis; naredimo nov seznam. Gremo prek podanega seznama in vanj dodajamo bodisi več elementov, bodisi enega.

def expanded(s): t = [] for e in s: if isinstance(e, tuple): t += [e[1]]*e[0] else: t.append(e) return t

Funkcija expand je delala težave vsem, ki niso hoteli slediti namigu in prebrati rešitve lanske domače naloge - ki je zahtevala praktično isto. Kdor je bil pripravljen malo brati, je napisal le

def expand(s): s[:] = expanded(s)

Pomembno je, da spreminjamo s, ne pa prirejamo imenu s. Tule smo vse elemente s-ja, s[:] zamenjali z elementi seznama, ki ga vrne expanded(s). Primer napačne rešitve bi bil

def expand(s): s = expanded(s)

Takšna funkcija le prireja neki lokalni spremenljivki s in ne spreminja tistega, kar smo poslali funkciji kot argument.

Rešitev na daljši način v najboljšem primeru zahteva zanko while.

def expand(s): i = 0 while i < len(s): if isinstance(s[i], tuple): s[i:i+1] = [s[i][1]] * s[i][0] i += 1

Gremo čez seznam in kadar naletimo na terko, jo zamenjamo (tako da prirejamo s[i:i+1] - rezini, ki vsebuje to terko) seznam z ustreznim število ponovitev naročene reči. Z nizi ne storimo ničesar.

Zanka teče prek seznama, ki se medtem spreminja, a to nas ne moti. Tekla bo, dokler i ne pride do konca seznama. Ob tem seveda naleti tudi na nize, ki smo jih na novo dodali, a nič hudega. Nize tako ali tako pustimo pri miru.

Funkcijo je mogoče nekoliko pospešiti tako, da preskočimo naknadno dodane nize.

def expand(s): i = 0 while i < len(s): if isinstance(s[i], tuple): t, e = s[i] s[i:i+1] = [e] * t i += t else: i += 1

Implode(d)

Potrebovali bomo indekse, zato moramo prek seznama s for i in range(len(s)) ali for i, e in enumerate(s). Kaj izberemo, je stvar okusa. Meni je bolj všeč drugo, tako da imamo v e "trenutni" element seznama.

Znotraj zanke bo if: če smo prišli do konca ponovitev določenega elementa, zapišemo, kolikokrat se je ponovil (ali, če se je le enkrat, zapišemo le niz). Konec ponovitev nastopi takrat, ko naslednji element ni enak temu (e != s[i+1]) ali pa, ko smo pravzaprav že pri zadnjem elementu (i == len(s) - 1). Pri tem moramo najprej preveriti ali gre za zadnji element, da pri e != s[i+1] ne bi prišlo do prekoračitve indeksa.

Spremenljivka c šteje, kolikokrat se je določen element ponovil. Vsakič, ko se ponavljanje prekine, ga postavimo nazaj na 1, sicer pa ga povečujemo.

def imploded(s): t = [] c = 1 for i, e in enumerate(s): if i == len(s) - 1 or e != s[i+1]: if c == 1: t.append(e) else: t.append((c, e)) c = 1 else: c += 1 return t

Imploded se da napisati na več načinov. Še en, precej drugačen, je tale: pri vsakem elementu (zunanja zanka) pogledamo, koliko enakih elementov mu sledi (notranja zanka).

def imploded(s): t = [] i = 0 while i < len(s): e = s[i] j = i + 1 while j < len(s) and s[j] == e: j += 1 if j == i + 1: t.append(e) else: t.append((j-i, e)) i = j return t

V dokaz, da se da tudi to: rešitev v eni vrstici:

from functools import reduce def imploded(s): return [x if v == 1 else (v, x) for v, x in reduce(lambda t,x: t + [(1, x)] if not t or x!=t[-1][1] else t[:-1] + [(t[-1][0]+1, x)], s, [])]
Последнее изменение: среда, 24 марта 2021, 23:04