Novakovi
Testi
Testi: testi-novakovi.py
Ogrevalna naloga
Napiši funkcijo najmlajsi_clan(oseba)
, ki kot argument prejme ime osebe in kot rezultat vrne starost
in ime najmlajše osebe v rodbini. Tako naj, recimo, klic najmlajsi_clan("Jurij")
vrne terko
(5, "Aleksander")
.
Nasvet: če ima oseba nima otrok, naj vrne svojo starost in ime. Če ima otroke, naj sprašuje njih, rezultate pa naj - čeprav gre za terke - preprosto primerja. Če primerjamo dve terki, bo manjša tista, ki je manjša glede na prvi element.
Rešitev
Kako bi to izvedli v predavalnici? Natančno tako, kot bo program: vsaka oseba bi vprašala vse svoje otroke, "koliko star je najmlajši član tvoje rodbine in kako mu je ime?" Med odgovori bi si zapomnil najmanjšega. Kot je namigovala že naloga, nam tule pomaga, da Python primerja terke tako, da najprej primerja prvi element, torej, v tem primeru, starost. V dodatno pomoč je bilo, upam, da smo prav ta vzorček gledali (v nerekurzivni obliki) pri reševanju prejšnje domače naloge.
Kot običajno bo oseba najprej zase rekla, da je najmlajša, nato pa spraševala svoje otroke, ali je morda kdo že mlajši. Reč ni čisto povsem smiselna (razen, če je morda kdo posvojil otroka, starejšega od sebe), a če nič drugega, služi kot smiselna prva vrednost - namesto "velike številke", ki smo jo kot prvo vrednost uporabili včasih.
Oklepaji v prvem prirejanju niso potrebni - vendar jih pišemo, da poudarimo, da gre za terko.
Ne spreglejte, da nismo nikjer rekli "če oseba nima otrok, naj kot
najmlajšega člana vrne sebe. To ni potrebno, saj se zanka v tem primeru pač ne
bo izvedla in naj
bo ostal, kar je - torej starost in ime
te osebe.
Za iskanje minimumov imamo sicer funkcijo min
, ki se je z
generatorji pokazala v še uporabnejši luči. Poskusimo takole: vsaka oseba išče
minimum tega, kar vrne najmlajsi_clan(otrok)
za vsakega od svojih
otrok.
Ups!?
Preberimo: po tem, ko je funkcija nekajkrat poklicala samo sebe, smo poskusili izračunati minimum praznega zaporedja. Ah, ja, to so tisti brez otrok. Tule jih bo očitno potrebno obravnavati ločeno.
Obvezna naloga
Napiši funkcijo mlajsi_od(oseba, n)
, ki vrne množico tistih
članov rodbine podane osebe, ki imajo manj kot n
let. Tako mora
mlajsi_od("Jurij", 10)
vrniti
{"Alenka", "Petra", "Aleksander"}
.
Namig: če ne znaš, preberi zapiske predavanj.
Rešitev
Tokrat začnimo s pogostimi napačnimi iskanji.
Nekoč, ko bom star in pameten, bom morda razumel, zakaj študenti ne razumejo rekurzije. Morda me je tale funkcija - praktično enako funkcijo sem ta teden precejkrat dobil po mailu, torej ste mnogi razmišljali na podoben način - pripeljala malo bližje k temu razumevanju.
Preden pogledamo, kaj je narobe zgoraj, naj ugibam: osnovna ideja je bila pravzaprav tale:
Imamo "seznam", v katerega doda oseba najprej sebe, če je dovolj mlada, nato pa pozove še svoje otroke, naj naredijo isto. Problem je tule: kdo bo naredil ta seznam? Glede na to, da ga potrebuje funkcija, ga bo najbrž naredila funkcija, ne?
To ne deluje zato, ker ima vsaka funkcija svoj seznam
.
Nepokorni otroci ne zlagajo sebe in svojih potomcev v isti seznam
,
temveč naredijo nov seznam in zlagajo vanj. Ta seznam sicer vrnejo
(return seznam
), vendar ta, ki je poklical funkcijo, z vrnjeno
množico ne naredi ničesar - pokliče mlajsi_od(otrok, n)
, rezultat
klica pa zavrže.
Navidezna rešitev tega je, da pač poskrbimo, da bodo vsi pisali v isto množico, takole:
Lani sem videl precej takšnih (napačnih) rešitev, letos ne. Morda niso prišle do mene, morda sem vas dovolj prestrašil in prepričal, da se tako ne da rešiti naloge. Tole bi sicer delalo - vendar le prvič. Ko bi funkcijo poklicali naslednjič, množica v začetku ne bi bila več prazna.
Rešitev v tej smeri sicer obstaja, vendar jo je najbrž težje razumeti kot pravo. Pa še precej grša je, ker uporablja globalne spremenljivke na način, s kakšrnim se izpostavlja nevarnostim, o katerih pri Programiranju 1 še ne znamo razmišljati.
Zdaj naša funkcija mlajsi_od
ni več rekurzivna. Njena naloga je,
da le zbriše množico seznam
in pokliče pravo rekurzivno funkcijo
mlajsi_od1
. Ta kliče samo sebe, tako kot prej, in napolni seznam.
Ker nihče ne gleda njene vrednosti, pa ga niti ne vrača. Pač pa ga na koncu
vrne mlajsi_od
.
To že dela, kar mora in tudi teste preživi, je pa grdo.
Druga rešitev, ki uporablja enak vzorec, je tale:
Razlika je v tem, da seznam
ni več globalna spremenljivka,
temveč ga funkcija mlajsi_od
naredi in si ga potem
mlajsi_od1
podaja kot argument. Funkcija mlajsi_od1
tako dobi tri argumente: ime osebe, mejno starost in množico, v katero mora
dopisovati člane rodbine. Pri igri v predavalnici bi bilo to ekvivalentno temu,
da bi si ti, ki se izprašujejo, pošiljali po predavalnici list (isti list!), na
katerega bi dopisovali rodbinske člane.
Na ta način bi nalogo rešili v jezikih, ki v delu z množicami niso tako spretni kot Python. Včasih kaj podobnega počnemo tudi v Pythonu, pri tej nalogi pa ni potrebno.
Rešitev je podobna funkciji, ki je zbrala vse rodbinske člane: naredili smo jo tako na predavanjih kot na vajah.
Kdor je res razumel, kako deluje ta funkcija, mu je ni bilo težko dopolniti tako, da je osebo dodala le, če je dovolj mlada.
Rešitev, ob kateri bi malo pogodrnjal, vendar ne bi smel preveč protestirati, saj je nisem prepovedal, je, da naredite množico, v kateri je vse rodbina, a nato obdržite le tiste, ki so dovolj mladi. Uporabimo torej funkcijo s predavanj in ji dodamo le še tole:
Še en komentar: v gornji funkciji smo imeli spremenljivko z imenom
seznam
. Dajati spremenljivkam imena, kot so
stevilo
, niz
, slovar
,
mnozica
ali seznam
ni preveč posrečena ideja.
Še posebej pa je to ponesrečeno, če ta seznam
v resnici ni seznam,
temveč množica.
Dodatna naloga
Daniel ima liho število let (85). Njegova hči Elizabeta ima sodo število (68). Njen sin Jurij ima liho (49). Njegov sin Franc ima spet sodo (30). Veriga Daniel -> Elizabeta -> Jutri -> Franc se izmenjuje glede na lihost in sodost števila let in je dolga štiri člene.
Napiši funkcijo naj_izmenicna_starost(oseba)
, ki vrne dolžino
najdaljše takšne izmenjujoče se verige, ki se prične pri podani osebi.
Rešitev
Spomnimo se, kako smo na predavanjih računali globino rodbine: vsaka oseba je vprašala vsakega otroka po globini njegove rodbine. Zapomnila si je največji odgovor in k njemu prištela 1, zase.
Ali, krajše
Dodati moramo še pogoj, pod katerim nas otrok zanima: če ima oseba sodo število let, mora imeti otrok liho in obratno. To se da lepo zaplesti
Če se spomnimo česa iz osnovne šole, pa vemo, da je vsota dveh sodih števil soda, dveh lihih prav tako; pač pa je vsota lihega in sodega liha. Torej preverimo le vsoto starosti; če je liha, ima nekdo sodo število let in drugi liho. Kdo je kdo, ni pomembno. Tako dobimo
Ali, krajše,