Rešitve s komentarji
Olimpijske medalje
Tabela kaže razvrstitev desetih držav glede na število medalj na olimpijskih igrah v letih 2016 in 2012. Vidimo, da so tri države na tej lestvici napredovale, tri pa nazadovale.
letošnje prejšnje
1 1 United States
2 3 ^ Great Britain
3 2 v China
4 4 Russia
5 6 ^ Germany
6 10 ^ Japan
7 7 France
8 5 v South Korea
9 9 Italy
10 8 v Australia
Napiši funkcijo napredek(s)
, ki kot argument prejme seznam števil v drugem stolpcu (npr. [1, 3, 2, 4, 6, 10, 7, 5, 9, 8])
, kot rezultat pa vrne par (terko) s števili, ki povesta, koliko držav je na lestvici napredovalo in koliko nazadovalo.
Funkcija mora delovati za poljubno dolge sezname, ne le za deset držav.
Rešitev
Želel sem si - a redko videl - da bi tule uporabili enumerate
. In, v splošnem, čim manj komplicirali, saj je bila naloga res lahka.
def napredek(s):
gor = dol = 0
for i, e in enumerate(s):
if i + 1 < e:
gor += 1
elif i + 1 > e:
dol += 1
return gor, dol
Človek ne jezi se
V poenostavljeni igri Človek ne jezi se ima vsak igralec eno figuro. Vsi začnejo na polju 0. Ko je igralec na vrsti, vrže kocko in premakne figuro za toliko polj, kolikor pokaže kocka. Če pri tem pride na polje, na katerem že stoji kateri drugi igralec, gre oni, drugi igralec na polje 0.
Napiši funkcijo clovek_ne_jezi_se(igralcev, meti)
, ki kot argument dobi število igralcev in zaporedje metov, kot rezultat pa vrne številke polj, na katerih se po teh metih nahajajo igralci.
Rešitev
Ta naloga je bila predvsem vaja iz spretnosti.
def clovek_ne_jezi_se(igralcev, meti):
pozicije = [0] * igralcev
for poteza, met in enumerate(meti):
igralec = poteza % igralcev
nova = pozicije[igralec] + met
pozicije = [0 if x == nova else x for x in pozicije]
pozicije[igralec] = nova
return pozicije
V prvi vrstici me zanima, ali znate elegantno pripraviti seznam iz ničel - [0] * igralcev
. Nato uporabimo enumerate
, da oštevilčimo poteze; igralec, ki je na potezi, je poteza % igralcev
. Večina študentov je namesto tega pisala
igralec = 0
for met in meti:
...
igralec += 1
if igralec == igralcev:
igralec = 0
ali
igralec = 0
for met in meti:
...
igralec = (igralec + 1) % igralcev
Tudi s tem ni nič narobe.
Nato izračunamo novo pozicijo tega igralca. Trenutne pozicije
zamenjamo z novo tabelo, v katero vse tiste elemente, ki so enaki nova
zamenjamo z 0. Seveda se da to narediti tudi na bolj zapleten način.
Končno zares prestavimo še tega igralca, pozicije[igralec] = nova
.
Kot ste videli pri reševanju te naloge, ne gre za preveč zapleteno reč, pač pa je lahko samo zelo zoprna in dolga, če si nekoliko neroden.
Zadnje liho
Napiši rekurzivno funkcijo zadnje_liho(s)
, ki vrne zadnje liho število v podanem seznamu ali None
, če v njem ni lihih števil.
Rešitev
Ta je zanimiva, ker sem si predstavljal tole rešitev.
def zadnje_liho(s):
if not s:
return None
t = zadnje_liho(s[1:])
if not t and s[0] % 2 == 1:
t = s[0]
return t
oziroma krajše različice, kot je
def zadnje_liho(s):
return s and (zadnje_liho(s[1:]) or s[0] % 2 and s[0]) or None
Tidve funkciji najprej preverita, ali je kakšno liho število v ostanku seznama. Če ga ni, preverita, ali je slučajno liho prvo.
Tako bi to naredil vsak zrel programer, ki je že kdaj delal v funkcijskih jezikih, kjer imamo pogosto neposreden dostop le do prvega, ne pa tudi do zadnjega elementa seznama.
Študenti so to obrnili drugače: če iščemo s konca, pač iščimo s konca.
def zadnje_liho(s):
if not s:
return None
if s[-1] % 2 == 1:
return s[-1]
return zadnje_liho(s[:-1])
To je seveda preprostejše in v Pythonu povsem legalno.
Največ dvakrat
Napiši funkcijo najvec_dve(s)
, ki v podanem seznamu pusti do dve (ne nujno zaporedni) pojavitvi vsakega elementa in pobriše vse nadaljnje. Funkcija mora vrniti None
; spreminja naj podani seznam.
Seznam ne vsebuje nujno števil, predpostaviti pa smete, da so njegovi elementi nespremenljivi (immutable).
Če imamo s = [4, 1, 2, 4, 1, 3, 3, 1, 2, 5, 4, 3, 7, 4]
, mora biti po klicu najvec_dve(s)
seznam s enak [4, 1, 2, 4, 1, 3, 3, 5, 7]
.
Rešitev
Ta vam je (pričakovano) dala vetra, zato sem jo dal bolj na konec. Ker morate spreminjati seznam, je potrebno uporabljati del
ali pop
- ne moremo kar tako sestaviti novega seznama. Pri tem pa imamo težavo: če gremo z zanko for
čez seznam, je brisanje elementov znotraj zanke zelo slaba ideja. Ne deluje. Primere smo videli.
Drugo, kar je zahtevala naloga, je, da vodite evidenco o tem, kolikokrat se je določen element že pojavil. Tu ste se večinoma spomnili uporabiti slovar, ali, še boljše, defaultdict
.
Rešitev je lahko, recimo ta
from collections import defaultdict
def najvec_dve(s):
kolikokrat = defaultdict(int)
i = 0
while i < len(s):
e = s[i]
kolikokrat[e] += 1
if kolikokrat[e] > 2:
del s[i]
else:
i += 1
Namesto for
uporabimo while
. Če element pobrišemo, ne povečamo števca, saj se bo naslednji element premaknil na mesto trenutnega.
Preprosteje je sestaviti nov seznam, katerega elemente potem prepišemo v s
.
def najvec_dve(s):
t = []
for e in s:
if t.count(e) < 2:
t.append(e)
s[:] = t
Ta rešitev sicer ni najhitrejša, ker uporablja count
. Lepše bi bilo spet uporabiti defaultdict
, vendar ... takole je pa krajše.
Ena od študentk se je spomnila domiselne rešitve. Preštela je viške, obrnila seznam, odstranila viške in ga spet obrnila. K njeni rešitvi lahko dodam le še, da bi lahko uporabila Counter
in tako dobimo:
from collections import Counter
def najvec_dve(s):
s.reverse()
for n, r in Counter(s).items():
for i in range(r - 2):
s.remove(n)
s.reverse()
Kudos za idejo.
Podjetje
Napiši razred Podjetje, s katerim je mogoče početi tole.
>>> megashop = Podjetje(1000)
>>> megashop.kapital
1000
>>> tralala = Podjetje(300)
>>> tralala.kapital
300
>>> megashop.prejme(200)
>>> megashop.kapital
1200
>>> megashop.placa(100, tralala)
>>> megashop.kapital
1100
>>> tralala.kapital
400
Rešitev
Želel sem si, da bi opazili, da je kapital
atribut, ne metoda, saj ga izpišemo, ne kličemo. Poleg tega me je zanimalo, ali boste znali pravilno narediti placa
, saj tam nastopata dva objekta, self
, in oni drugi objekt, kateremu self
plačuje. Če to dvoje vemo/znamo, je naloga čisto preprosta.
class Podjetje:
def __init__(self, kapital):
self.kapital = kapital
def prejme(self, koliko):
self.kapital += koliko
def placa(self, koliko, komu):
self.kapital -= koliko
komu.prejme(koliko)