Testi

Testi: testi-novakovi-starsi.py

Ogrevalna naloga

Napiši funkcijo starost_starsa(oseba), ki vrne starost podane osebe, ko je imela prvega otroka. Ta funkcija najbrž ne bo rekurzivna (lahko pa je, če želiš). Če oseba nima otrok, naj funkcija vrne 999.

Nato napiši funkcijo najmlajsi_stars(oseba), ki vrne najnižjo starost, pri kateri je imel kdo iz rodbine podane osebe otroka. Klic najmlajsi_stars("Daniel") mora vrniti 17, saj je imela Elizabeta prvega otroka že pri sedemnajstih (in nihče ni imel otroka še mlajši). Prav tako in iz istega vzroka mora tudi najmlajsi_stars("Elizabeta") vrniti 17. Klic najmlajsi_stars("Jurij") vrne 19 (zaradi Jurija). Klic najmlajsi_stars("Barbara") vrne 999, ker nihče v Barbarini rodbini ni imel otrok (z drugimi besedami, Barbara nima otrok).

Ta, druga funkcija mora biti rekurzivna.

V rodbini se je pojavil tudi Kajn, kot Adamov oče. Opravičujem se za to historično netočnost, ki pa je potrebne za zaznavanje določene možne napake (ki sem jo naredil tudi sam in je kmalu ne bi opazil, ker ... za opažanje te napake potrebujemo še Adamovega prednika).

(Opomba: beseda starš se mi zdi grozna. Vendar je roditelj, ki se je uporabljala pred njo, še neumnejša, saj ne gibanja proti spolni diskriminaciji ne najsodobnejša medicina še niso proizvedla nobenega "roditelja".)

Rešitev

Če nekdo nima otrok, funkcija vrne 999. Sicer mora vrniti starost osebe, od katere odšteje starost najstarejšega otroka.

def starost_starsa(oseba):
    if not otroci(oseba):
        return 999
    else:
        return starost[oseba] - max(starost[otrok] for otrok in otroci(oseba))

Druga funkcija je tipična rekurzivna funkcija, kakršne smo delali na predavanjih. Najprej predpostavimo, da je najnižja starost kar ta, ki jo dobimo pri tej osebi. Potem (rekurzivno) povprašamo pri otrokih in shranimo morebitne nižje starosti.

def najmlajsi_stars(oseba):
    m = starost_starsa(oseba)
    for otrok in otroci(oseba):
        ss = najmlajsi_stars(otrok)
        if ss < m:
            m = ss
    return m

Obvezna naloga

Napiši rekurzivno funkcijo starsi_pred(oseba, n), ki vrne množico imen vseh oseb iz rodbine oseba, ki so imele otroka, še preden so bile stare n.

Če ti bo lažje, lahko najprej napišeš funkcijo starsi_pred_20(oseba), ki vrne množico imen vseh oseb, ki so imele otroka, preden so bile stare 20. (Za to funkcijo ni testov. To je samo za prvi korak.)

Rešitev

Tole je zelo podobno kot preštevanje rodbine. Tisto je izgledalo tako:

def velikost_rodbine(oseba):
    v = 1
    for otrok in otroci(oseba):
        v += velikost_rodbine(otrok)
    return v

Tule bomo imeli namesto števila članov rodbine njihova imena. Če bi hoteli nabrati vso rodbino, bi rekli

def starsi_pred(oseba, n):  # Tole še ni prava rešitev!
    s = {oseba}
    for otrok in otroci(oseba):
        s |= starsi_pred(otrok, n)
    return s

Oseba najprej da v množico sebe, potem "priunija" vse, kar vrnejo otroci.

Vendar tako dobimo celotno rodbino, ne le tistih, ki so imeli otroka, ko so bili mlajši od n. Vse, kar moramo spremeniti, je, da oseba da v množico sebe le, če je imela otroka dovolj mlada. Recimo tako:

def starsi_pred(oseba, n):  # Tole še ni prava rešitev!
    if starost_starsa(oseba) < n:
        s = {oseba}
    else:
        s = set()
    for otrok in otroci(oseba):
        s |= starsi_pred(otrok, n)
    return s

Ali, malo drugače obrnjeno:

def starsi_pred(oseba, n):  # Tole še ni prava rešitev!
    s = set()
    if starost_starsa(oseba) < n:
        s.add(oseba)
    for otrok in otroci(oseba):
        s |= starsi_pred(otrok, n)
    return s

Pogosto napačno razmišljanje

Tole je precej pogosta napačna rešitev (ali vsaj napačen korak, ki ne vodi k rešitvi).

def starsi_pred(oseba,n):
    s = set()
    if starost_starsa(oseba) < n:
         s.add(oseba)
    for otrok in rodbina[oseba]:
         starsi_pred(otrok,n)
    return mnozica

Čeprav je tole skoraj enako pravilni rešitvi, je čisto drugače v tem smislu, da ta, ki razmišlja tako, najbrž razmišlja precej narobe. Napaka je v tem, da si predstavljamo, da bodo vsi klici funkcije starsi_pred dodajali v isto množico. Ne, ob vsakem klicu funkcije imamo nove lokalne spremenljivke. Vsaka funkcija ima svojo množico s.

Da je nekaj narobe, vidimo tudi po tem, da znotraj funkcije kličemo starsi_pred, vendar rezultata nikamor ne shranjujemo - kar zavržemo ga.

Kot vidimo, je sprememba, ki jo moramo narediti preprosta - tisto, kar vrača funkcija, moramo dodajati v množico.

Dodatna naloga

Funkcija najmlajsi_starsa ima nekoliko zavajajoče ime, saj vrne starost, ne pa imena oseba.

Napiši funkcijo ime_najml_starsa(oseba), ki vrne ime osebe, ki je imela najmlajša otroke. Klic ime_najml_starsa("Danijel") mora vrniti "Elizabeta". Če je takih oseb več, naj vrne ime prve po abecedi. Če ni nobene, naj vrne None.

Namig: ta funkcija morda ne bo rekurzivna. Pač pa napiši rekurzivno funkcijo, ki vrne par (starost, ime). Funkcija ime_najm_starsa(oseba) naj potem pokliče to funkcijo.

Rešitev

Težava je v tem, da mora funkcija vračati ime, primerjati pa mora števila. To najlažje rešimo tako, da napišemo funkcijo, ki vrača oboje.

def ime_najml_starsa0(oseba):
    jaz = (starost_starsa(oseba), oseba)
    potomec = min((ime_najml_starsa0(otrok) for otrok in otroci(oseba)),
                   default=(99, None))
    return min(jaz, potomec)

jaz so podatki o osebi, potomec pa o tistem članu rodbine, ki je imel najmlajši otroka. Vzamemo manjšo od teh.

Tisti default=(99, None) priskrbi vrednost, ki jo min vrne, kadar je seznam otrok prazen.

Vem, znotraj min uporabljam generatorje in tudi default je nov trik. Vendar gre tudi brez tega; sploh pa je to že dodatna naloga. :)

Nato le pokličemo to funkcijo in vrnemo prvi element.

def ime_najml_starsa(oseba):
    return ime_najml_starsa0(oseba)[1]

Zelo dodatna naloga

Na predavanjih smo napisali tole funkcijo, za katero ne vemo, kaj dela.

def kaj_dela(oseba):
    velikost = 0
    for otrok in otroci(oseba):
        velikost += kaj_dela(otrok)
    return abs(velikost - 1)

Ugotovi, kaj dela; rezultat svojega razmišljanja lahko napišeš kot komentar pod ostale funkcije.

Rešitev

Pojma nimam. Morda nič takšnega, kar bi se dalo pametno opisati. :)

마지막 수정됨: 목요일, 25 3월 2021, 8:29 PM