7. hét: osztályok

Czirkos Zoltán · 2019.08.29.

Gyakorlófeladatok az előadás anyagához kapcsolódóan.

1. Osztályok

Elmozdulás egy adott ponttól

Készíts függvényt, mely egy pont x és y koordinátáival tér vissza. A függvény paraméterként kapja egy pont x és y koordinátáját (egy pontot), valamint egy szöget és egy távolságértéket. Számítsd ki a visszatérési értékként szereplő pont x és y koordinátáit, hogy az a megadott ponttól meghatározott szögben és távolságban legyen.

Elforgatás egy pont körül

Készíts függvényt, amely egy x és y koordinátával rendelkező pontot elforgat egy másik adott pont körül, adott szöggel! A függvény visszatérési értéke az elforgatott pont legyen. (A forgatáshoz való képletet megtalálod a függvénytáblában is.)

Rudak hossza

Egy gyárban fémrudakat gyártanak. A megmunkálás pontatlansága miatt azonban ezek hossza kicsit eltérő: pl. egy 1 méteresnek szánt rúd 999 mm és 1001 mm között bármekkorára sikerülhet. Ha két ilyen rudat egymás mögé teszünk, akkor az összegzett hosszuk valahol 1998 és 2002 mm között lesz.

  • Definiálj osztályt, amely egy rúd minimális és maximális hosszát tárolja!
  • Írj egy függvényt, amely paraméterként kapja két rúd adatait, és visszatérési értéke egy rúd, amely ezek összege (egymás mögé tett rudak hossztartománya).
  • Írj függvényt, amely visszaadja egy paraméterként kapott rúd átlagos hosszát!
  • Egészítsd ki ezt teljes programmá, amelyben létrehozol egy 999-1001 mm-es, és egy 498-502 mm-es rudat. Számítsa ki a program a függvényekkel, hogy mekkora ezek összege minimálisan, maximálisan és átlagosan!

Vektorok I.

Egy programban kétdimenziós vektorok adatait kell tárolni. Ilyenek lehetnek a sebességek: vx vízszintes irányú, vy függőleges irányú sebességek adják a v sebességvektort.

  • Definiálj osztáyt, amely egy sebességvektort tárol!
  • Írj függvényt, amely paraméterként egy sebességvektort kap, és visszatérési értéke a vektor hossza (Pitagorasz-tétel)!
  • Írj függvényt, amely paraméterként két sebességvektort kap, és visszatérési értéke az összeg vektor (komponensenként)!
  • Egészítsd ki mindezt főprogrammá, amelyben egy (1 m/s, 2 m/s) és egy (-0,5 m/s, 3 m/s) sebességvektort összegzel, és utána kiszámítod, az eredő vektornak mekkora a hossza! Írd ki az összes kiszámolt adatot!

Vektorok II.

Írd meg úgy az előző programot, hogy

  • Kiíró függvény helyett sztringgé alakító operátort használsz: __str__.
  • Hosszt meghatározó függvény helyett abszolút érték operátort használsz: __abs__. (Ezt hívja a Python az abs(valami) kifejezés kiértékelésekor.)
  • Összeadó operátort használsz: __add__.

3D vektorok

Az előadáson bemutatott törtes példa alapján írj egy programot, amelyik háromdimenziós vektor típust képes kezelni! Tudjon vektorokat kiírni, összeadni, kivonni; számítsa ki két vektor skaláris szorzatát!

Dátumok, öröknaptár I.

Írjunk programot, amely egy osztályban dátumot tárol: év, hónap, nap. Kezeljék ezeket függvények:

  • datum_kiir(d): kiírja a dátumot év.hónap.nap formában.
  • datum_ev_napja(d): megmondja, az év hányadik napja. Vegye figyelembe a szökőéveket! (Ehhez csak elő kell szedni a 4. gyakorlat feladatát – tekinthetjük azt akár kidolgozottnak is.)
  • datum_kivon(d1, d2): megmondja, hány nap telt el d2-től d1-ig, ahol d1 a kisebbítendő, d2 a kivonandó.
  • milyen_nap(d): megmondja, milyen napra esik az adott dátum. 1=hétfő, 7=vasárnap. 1900. január 1. hétfőre esett.

Dátumok, öröknaptár II.

Írd meg az előző feladatot __str__, __sub__ operátorok használatával!

Átfedő körök

Egy geometriai programban körök adatait kell tárolni: középpont (x, y koordináta) és sugár. Ezek valós számok.

  • Definiálj osztályt, amelyben egy kör adatai eltárolhatóak!
  • Írj függvényt, amely paraméterként kap két kört, és megmondja, hogy azok átfedik-e egymást! (Ez akkor van, ha a középpontjaik Pitagorasz-tétellel számolható távolsága kisebb, mint a sugaraik összege.)
  • Írj függvényt, amely beolvassa a billentyűzetről a középpontot és a sugarat, és visszatérési értéke egy ilyen tulajdonságú kör.
  • Egészítsd ki ezt teljes programmá, amelyben beolvasod két kör adatait, és megmondod, hogy azok átfedik-e egymást!

Időpontok

Definiálj típust, amelyben egy időpontot tudsz tárolni, külön óra (0...23), perc (0...59) és másodperc (0...59) értékekkel!

Írj függvényt, amelyben paraméterként egy sztringet veszel át, benne egy időponttal! Értelmezd a sztringet, majd add vissza az időpontot az előbb definiált típusban! A sztringben az időpont az alábbi három forma egyikében lesz:

  • 23:17:06 – óra, perc, másodperc, mindegyik két számjeggyel;
  • 15h 09m 53s – itt is óra, perc, másodperc, mindegyik két számjeggyel;
  • 10:15 AM – itt az óra és a perc két-két számjeggyel, a másodperc pedig nincs megadva, 0-nak kell tekinteni. Ez a formátum 12 órás; 12:00 AM = éjfél, 08:00 AM = reggel 8, 12:00 PM = dél, 05:00 PM = a délután 5 órai tea időpontja, azaz 17 óra.

Írj főprogramot, amelyben feldolgozod a függvényeddel a fenti három példa időpontot, és beteszed őket három külön változóba!

Megoldás

Az elsőt formátumot a hátsó kettőspontról megismerni. A másodikat a h betűről. A harmadikat az M betűről. Az AM-PM problémát legrövidebben talán úgy lehet kezelni, ahogy a lenti példa mutatja: mod 12, és utána PM esetén +12. A *-gal jelölt részeket nem kérte a feladat.

A félév vége felé lesz szó egy egész más eszközről, ami ilyen jellegű problémákat segít megoldani (reguláris kifejezések). Ez a mintamegoldás a félév elején bemutatott eszközökből építkezik.

class Idopont:
    def __init__(self, ora, perc, mperc):
        self.ora = ora
        self.perc = perc
        self.mperc = mperc
    
    def __str__(self):  # *
        return "{:02}:{:02}:{:02}".format(self.ora, self.perc, self.mperc)

def sztringbol_idopont(s):
    if s[5] == ':':
        return Idopont(int(s[0:2]), int(s[3:5]), int(s[6:8]))
    if s[2] == 'h':
        return Idopont(int(s[0:2]), int(s[4:6]), int(s[8:10]))
    if s[7] == 'M':
        ora = int(s[0:2]) % 12
        if s[6] == 'P':
            ora += 12
        return Idopont(ora, int(s[3:5]), 0)
        
    raise ValueError("érvénytelen formátum")    # *

def main():
    i1 = sztringbol_idopont("23:17:06")
    i2 = sztringbol_idopont("15h 09m 53s")
    i3 = sztringbol_idopont("12:15 PM")
    
    print(i1, i2, i3, sep="\n") # *

main()

2. Operátorok

Időpont – operátorokkal

Indulj ki az időpontos laborfeladat megoldásából, és módosítsd azt a következőképpen!

  • Az ido_kiir() függvény helyett írj sztringgé konvertáló függvényt.
  • Az ido_hozzaad() függvény helyett írj összeadó operátort.
  • Az ido_eltelt() függvény helyett írj kivonó operátort.
  • Az ido_kivon() függvény is a kivonás operátorral kell működjön. Módosítsd azt, hogy ellenőrizze a típust! Időpont-időpont esetén percet kell adjon, időpont-perc esetén időpontot.

Végül pedig írj olyan konstruktort, amellyel nem csak Idopont(ora, perc), hanem Idopont("ora:perc") formában is inicializálható egy időpont objektum! Ehhez is típus szerinti esetszétválasztást kell csinálnod, mint az előadás törtes példájában.

Megoldás
class Idopont:
    def __init__(self, p1, p2=None):
        if type(p1) is int and type(p2) is int: # int, int
            self.ora = p1
            self.perc = p2
        elif type(p1) is str and p2 is None: # str
            elemek = p1.split(":", 1)
            self.ora = int(elemek[0])
            self.perc = int(elemek[1])
        else:
            raise TypeError("Időpont: rossz konstruktorhívás")

    def __str__(self):
        return "{:2}:{:02}".format(self.ora, self.perc)

    def __add__(self, jobb):
        novelt = self.perc+jobb
        perc = novelt%60
        ora = self.ora + novelt//60
        ora %= 24
        return Idopont(ora, perc)

    def __sub__(self, jobb):
        if type(jobb) is Idopont:
            return (self.ora - jobb.ora)*60 + (self.perc - jobb.perc)
        elif type(jobb) is int:
            csokkentett = self.perc-jobb
            perc = csokkentett%60
            ora = self.ora + csokkentett//60
            ora %= 24
            return Idopont(ora, perc)
        else:
            raise TypeError("Hibás operandusok")

def main():
    i = Idopont(22, 2)
    print(i) # 22:02
    i = i+40
    print(i) # 22:42
    i = i+20
    print(i) # 23:02
    i = i+60
    print(i) #  0:02

    print(Idopont(20, 00) - Idopont(14, 30))

    i = Idopont(0, 2)
    print(i) #  0:02
    i = i-60
    print(i) # 23:02
    i = i-20
    print(i) # 22:42
    i = i-40
    print(i) # 22:02

main()

Kerítés – operátorokkal

Indulj ki a kerítéses laborfeladat megoldásából!

Az egyenlo() függvény helyett írj __eq__ (egyenlő) és __ne__ (nem egyenlő) operátorokat! A beolvas() függvény helyett írj olyan konstruktort, amelyik sztringből képes Pont objektumot létrehozni! Végül pedig, a tav() helyett írj __sub__ kivonás és __abs__ abszolút érték (hossz) operátort! Így a távolság abs(p1 - p2) kifejezéssel számítható majd.

3. Összetett adatszerkezetek

Kártyapakli I.

Kártyás játékot írunk. Definiálj egy olyan osztályt, amely tárolhatja egy kártya adatait (szín: pikk, treff, … és szám: A, 2, 3, … J, Q, K). Tölts fel egy listát egy pakli kártyáival. Utána keverd meg a tömböt. A keverő algoritmus ne cserélje feleslegesen sokszor a lista elemeit! Végül írd ki, milyen sorrendben szerepelnek a kártyák a megkevert pakliban.

Kártyapakli II.

Definiálj egy olyan osztályt, amely tárolhatja egy kártya adatait (szín: pikk, treff, … és szám: A, 2, 3, … J, Q, K)!

Írj függvényt, amely megmondja egy pakli kártyáról (kártyák listájáról), hogy:

  • Hiányos-e a pakli,
  • Van-e benne dupla lap (kétszer ugyanaz)!

Bankautomata I.

Pénzvisszaadós automatába kell egy olyan programrészt írnunk, amelyik a visszajárót számolja ki. Írjunk egy programrészt, amely egy adott pénzösszegről kiírja, hogy hogyan lehet azt a legkevesebb papírdarabbal/fémkoronggal kiadni!
Például: 1415 Ft = 1000 Ft + 2×200 Ft + 10 Ft + 5 Ft.

Megoldás

A megoldásban csökkenő sorrendben vizsgáljuk a címleteket, és mindegyikből kiadunk annyit, amennyit csak lehet. A csökkenő sorrendet úgy állítjuk elő, hogy a listát, amely a címleteket tartalmazza, eleve csökkenő sorrendbe rendezve építjük be a programba. Ez egy ún. mohó algoritmus – mindig a legnagyobbat próbálja lépni a megoldás felé.

A kiadott darabszám kiszámításához egész osztást használhatunk. Pl. 11000 // 5000 = 2, azaz 11 ezer forinthoz kettő darab 5000 forintos bankjegy kell. A maradék 1000, amit más címlettel kell megoldani.

Bankautomata II.

Ez a feladat az előző folytatása. Most az automata rekeszei végesek. Tárold el azt is, hogy melyik bankjegyből és érméből épp mennyi van! Írj programot mohó algoritmussal, amelyik úgy ad pénzt, hogy ezt figyelembe veszi!

Megoldás

Összetartozó adat a rekeszben található címlet és a hozzá tartozó darabszám, pl. hogy 20000 forintosból 20 darab van. Hogy mindkettő egész szám (a forint és a darab), senkit ne tévesszen meg, ez nem lista! Minden rekeszhez egy objektum tartozik. A rekeszekből viszont sok van, és egyformák: ezért a rekeszeknek egy listája lesz. A használt adatszerkezet objektumok listája.

A ciklusban először egy osztással kiszámoljuk, hogy mennyi kellene az adott címletből. Utána pedig megnézzük, van-e annyi egyáltalán. Ha nincs, akkor csak kevesebbet adunk ki.

A mohó algoritmus amúgy nem tökéletes megoldása a feladatnak. Pl. ha 6000-t kérünk, és van 5000-es és 2000-es, de nincs 1000-es, akkor ki akar adni egy 5000-est, és utána megáll. Nem veszi észre, hogy 3 darab 2000-essel megoldható lenne a kérés. A tökéletes megoldáshoz az ún. visszalépéses keresést kellene alkalmazni, amelyhez a tudnivalók majd később szerepelnek az előadáson.

Black Jack I.

Készítsünk python osztályokat a Black Jack játék modellezésére!

A Black Jackben (magyarul 21) a játékosok célja, hogy a náluk lévő lapok értékének összege minél közelebb legyen a 21-hez

Minden játékos 2 lappal kezd. Ezután 2 lehetőségük van: kérnek új lapot (ekkor ennek értéke hozzáadódik a meglévőkhöz), vagy megállnak.

A lapok értéke a számos lapok esetén a nyomtatott számérték, figurák esetén 10 (egyszerűsített eset).

Írd meg a következő osztályokat!

  • Black Jack kártya, mely tárolja a lap egyediségét biztosító szín-érték párt és egy számértéket ami a Black Jackben használt érték. (ehhez felhasználhatod a Kártyapakli I. megoldását)
  • Játékos, mely névvel és húzott kártyákkal rendelkezik.
  • Pakli, mely a még ki nem húzott kártyákat tárolja.

Írd meg a következő függvényeket!

  • laphúzás: a Pakliból egy véletlen kártyát egy Játékoshoz ad
  • kiértékelés: megmondja egy Játékosról, hogy a jelenleg nála lévő lapoknak mennyi az összértéke

Black Jack II.

Az előző feladatban elkészítettünk egy objektummodellt és hozzá tartozó működést a Black Jack játékhoz.

Az ilyen tervezésnek az előnye, hogy innentől kezdve ezen alapokra építkezve több programot is készíthetünk, a játékkal kapcsolatosan, különösebb nehézség nélkül. Az adatmodell és a fent megvalósított működés újrahasznosítható.

Például:

  • Konzolos Black Jack játék: A játékosok sorban jönnek és mindig eldöntik (input), hogy kérnek-e még lapot, amikor már senki sem kér a program kiírja a nyertest (akinek a pontja alulról legközelebb van 21-hez).
  • Black Jack szimulátor a legjobb stratégiára: Csináljunk robotokat! Ehhez ki kell egészítenünk a Játékos osztályt. Adj hozzá a Játékoshoz egy mezőt mely az a számérték, ami után a Játékos már nem kér lapot. Ez lesz a Robot stratégiája. Addig kér lapot amíg el nem éri ezt az értéket. Készíts programot amiben 2 ilyen Játékos versenyez (itt ugye a játék futásához nem kell user input)! Legyen a stratégiájuk különböző. Futtass le 1000 játékot! Melyik értéknél érdemesebb megállni?