Želva z žigom
Naloga
Želvi dodaj metodi
stamp()
, ki naredi odtis želve, torej izriše želvo, ki ostane izrisana tudi, ko gre želva naprej), inclearStamps()
, ki pobriše vse odtisnjene želve s trenutne slike
Program | t.forward(10)
t.stamp()
t.left()
t.forward(100)
t.turn(45)
t.forward(20)
t.stamp()
t.right()
t.forward(40)
t.left()
t.forward(40)
t.right()
t.forward(40)
t.stamp()
t.right()
t.forward(40)
t.hide()
| nariše | . Če nato rečemo clearStamps() , želvice izginejo: |
Zahteva: metodi morata pravilno delovati tudi, če imamo več želv, ki se odtiskujejo in brišejo svoje odtise!
Namig: stamps
naj vse, kar riše, shranjuje v seznam, clearStamps
pa pobriše narisano.
Tolažba: metodi imata po štiri vrstice, in še v __init__
dodamo eno, pa smo.
Rešitev
Najprej je bilo potrebno v funkcijo __init__
dodati seznam, v katerega bomo shranjevali odtise želv. Dodamo, recimo
Funkciji za odtis želve in brisanje odtisov sta takšni
(Funkcija je dve vrstici daljša, kot sem obljubil, ker oba kroga zaradi pregledanosti najprej spravim v body
in head
ter od ondod v seznam. Šlo bi tudi self.stamps.append(risar.krog(self.x, self.y, 5, risar.zelena, 3))
...)
Način, na katerega brišemo odtise, sicer ni čisto pravilen, saj so odtisi še vedno na sliki, le vidni niso. Lepše bi bilo turtle.risar.widget.scene.removeItem(stamp)
, a to očitno presega namen vaje in količino informacije, ki sem vam jo dal.
Druge rešitve
Pogosto sem videval tole
self.x
jemljemo kar self.body.x()
in tako naprej, se izognemo temu, da v okviru te funkcije računamo položaj glave in telesa. Ni mi všeč, nimam pa kakih otipljivih argumentov, zakaj. Najbrž je ravno tako OK.
Rešitev, ki mi res ni všeč, bila pa je precej pogosta, je tale
Razlog, da je ne maram, je v tem, da za seboj ne pušča le preprostih objektov, krogov, temveč kar cele želve. Njena prednost bi se pokazala, če bi se nenadoma premislili in želv ne bi več risali s krogi temveč s kvadrati: metode stamp
nam kljub temu ne bi bilo potrebno spreminjati, saj ničesar ne riše sama. Slabost je v tem, da so želve bolj zapletena reč od krogov in požrejo več pomnilnika. Res pa nam je v tem primeru za teh par bajtov popolnoma vseeno. Moji argumenti proti tej rešitvi so bolj načelni kot praktični.
Nerodnosti
Pustite self.head
in self.body
pri miru! Načelen razlog je v tem, da dodajamo odtise in to nima zveze z glavo in telesom (razen tega, da slučajno enako izgleda). Praktičen je v tem, da se na ta način prehitro zaplezate in morate reševati probleme z želvo, ki izginja.
Tipičen primer takega gre nekako tako:
self.update()
, tako da je bila želva do prvega premika celo narisana v zgornjem levem kotu.
Spolzka tla pa so tule. V __init__
dodamo
Metodo stamp
napišemo kot
stamps
je trenutna slika želve. Se pravi: stamps
vedno vsebuje vse odtise in še želvo za povrh. Na to moramo paziti pri brisanju, ki ga pravilno napišemo takole:
Resne napake
Zelo narobe je tole
Poleg tega sem kot nepravilno rešene naloge štel tistih nekaj rešitev, ki so imele namesto metod funkcije, saj je bil cilj naloge učenje objektnega programiranja.
Izpeljevanje
Nekateri ste iz razreda Turtle
izpeljali nov razred, ki zna delati odtise. To se pravilno naredi tako:
Pomembno je torej, da ima izpeljani razred svoj __init__
, ki pokliče podedovanega, nato pa nastavi self.stamps
. O tem, kako se kliče podedovane metode, kadar jih povozimo z novimi, na prejšnjih predavanjih nisem govoril, saj ni bilo časa. Poleg tega način, na katerega sem to naredil zgoraj, ni več v modi, vendar onega, pravega, ne moremo uporabiti, ker bi bilo za to potrebno malenkost spremeniti razred Turtle
. Kogar zanima, naj si ogleda; na predavanjih pa bomo že še omenili.
Nekateri so namesto, da bi definirali svoj __init__
, naredili takole
stamps
lastnost razreda in ne objekta. Tudi to je nekaj, o čemer na predavanjih nisem posebej govoril in niti ne nameravam; to je ekvivalentno razrednim spremenljivkam (class variable) v C++ in presega predavanja iz Programiranja 1. Za tiste, ki jih zanima, pa je tu primer.
Zdaj je l
skupen vsem objektom razreda A
, k
pa ima vsak objekt posebej. Preverimo: