13. hét: adatszerkezetek

Czirkos Zoltán · 2023.11.22.

Listák leképezése és szűrése. A dict és set típusok használata.

Felkészülés a laborra:

1. Ötödik kis ZH

A hétfői laborokra csúszott az ötödik kis ZH. Fájl használat a témakör.

2. Listák leképezése és szűrése

Alakítsd az alábbi programrészeket list comprehension kifejezéssé!

l = []
for i in range(0, 10):
    l.append(i * 2)
l = []
for i in range(100):
    if i % 10 == 3:
        l.append(i)
l1 = [43, 15, 48, 59, 33, 72, 11, 65, 95, 34]
l2 = []
for i in l1:
    if i % 2 == 1:
        l2.append(i)
l1 = [43, 15, 48, 59, 33, 72, 11, 65, 95, 34]
l2 = []
for i in l1:
    if i % 2 == 1:
        l2.append(True)
    else:
        l2.append(False)

# Vigyázz, ebben átverés az if.
l1 = ["alma", "körte", "barack", "szilva", "ananász"]
l2 = []
for s in l1:
    if s[0] == 'a':
        l2.append(s)
l1 = ["alma", "körte", "barack", "szilva", "meggy"]
l2 = []
for s in l1:
    l2.append(len(s))
print(l2)

3. Szász Pál verse

Alább olvashatod Szász Pál versét. A vers érdekessége, hogy a szavak hossza, azaz a betűik száma kiadja a π tizedesjegyeit. Nem → 3, a → 1, régi → 4, és így tovább.

Nem a régi s durva közelítés,
Mi szótól szóig így kijön
betűiket számlálva.
Ludolph eredménye már,
ha itt végezzük húsz jegyen.
De rendre kijő még tíz pontosan,
azt is bízvást ígérhetem.

A feladatod: megkapod a verset az alább látható formában; a sorokat / jellel elválasztva, ahogy azt irodalom óráról ismerjük. Írd ki a vers alapján a π tizedesjegyeit! Használj lista szűrés és leképezés műveleteket!

vers = "Nem a régi s durva közelítés, / Mi szótól szóig így kijön / betűiket számlálva. / Ludolph eredménye már, / ha itt végezzük húsz jegyen. / De rendre kijő még tíz pontosan, / azt is bízvást ígérhetem."

A program ezeket a számjegyeket kell majd kiírja:

3141592653589793238462643383279
Megoldás
vers = "Nem a régi s durva közelítés, / Mi szótól szóig így kijön / betűiket számlálva. / Ludolph eredménye már, / ha itt végezzük húsz jegyen. / De rendre kijő még tíz pontosan, / azt is bízvást ígérhetem."
 
szamjegyek = [
    len(szo.rstrip(",."))
    for szo in vers.split()
    if szo != "/"
]
print(*szamjegyek, sep="")

A vers.split() szavakra töri a sztringet a szóközök mentén. Ezzel kapunk egy ehhez hasonló listát:

["Nem", "a", "régi", "s", "durva", "közelítés,", "/", "Mi", ...]

Ebből nekünk a perjelet tartalmazó sztringek nem kellenek, mert azok nem részei a versnek, csak a sorok határait jelölik. Azaz beteszünk egy szűrést: if szo != "/".

A kapott sztringek felesleges írásjeleket is tartalmaznak, amelyek pedig a szavaknak nem részei. Ezeket le kell vágni. Úgyhogy a feldolgozott szavakból, for szo in vers.split(), kivágjuk azokat. Ezt a sztringek .rstrip() tagfüggvénye tudja megtenni, amelynek most megadjuk, hogy a vesszők és a pontok nem kellenek. Így kapnánk az alábbi listát:

["Nem", "a", "régi", "s", "durva", "közelítés", "Mi", ...]

Ez már nem tartalmaz sem szükségtelen sztringeket, sem szükségtelen karaktereket. Vagyis ebben kell egy leképezést végezni: minden szó helyett annak hosszát tekinteni. Ezért a szo.rstrip(".,") kifejezést, amely a szavakat adja, még odaadjuk a len függvénynek. És készen is vagyunk, ez a lista kerül a szamjegyek változóba:

[3, 1, 4, 1, 5, 9, 2, ...]

Az egész feladat egyébként megoldható egyetlen sorban, mert ha az első kifejezésbe írt szo.rstrip(",.") kifejezést rögtön len()-be csomagoljuk, akkor eleve a szóhosszak listája áll elő:

szamjegyek = [len(szo.strip(",.")) for szo in vers.split() if szo != "/"]

4. Charlie

Charlie fagylaltot árul: jelenleg pisztácia, vanília, tutti-frutti, karamell, rumos dió és kávé a választék. A fagyit íz alapján kérhetik a gyerekek, egyszerre csak egy gombócot. A készlet véges, minden gombóc eladásával értelemszerűen eggyel csökken.

Tárold el a rendelkezésre álló mennyiségeket egy szótárban, azaz dict tárolóban!

fagyik = {
    "pisztácia": 0,
    "vanília": 3,
    "tutti-frutti": 8,
    "karamell": 4,
    "rumos dió": 5,
    "kávé": 9,
}

Írj programot, amely a vásárlásokat kezeli! Olvasd be a vásárlásokat (ízeket) üres sorig. Keresd meg az előbb megírt függvénnyel a kapott ízt, és jelezd a vásárlás eredményét: sikeres, vagy kifogyott (volt, de 0-ra csökkent), esetleg nem is volt!

vanília
kösz, öcsi!

pisztácia
pisztácia kifogyott!

csokoládé
csokoládé nem is volt!

5. Zöldséges

Adott egy zöldséges alábbi raktárkészlete és árai:

keszlet = {
    "banán": 6,
    "alma": 31,
    "narancs": 32,
    "körte": 15
}

arak = {
    "banán": 100,
    "alma": 80,
    "narancs": 120,
    "körte": 90
}

Első feladat

A programodban egy vásárlást kell kezelni. A felhasználó megadja, hogy mit szeretne venni – több zöldség vagy gyümölcs nevét is megadhatja, egészen üres sorig tart a bemenet. Ezután írja ki a program, hogy mennyi a vásárlás végösszege! Természetesen csökkenjen is a készlet, vagy ha olyan tételt kért, amiből nincsen, akkor jelezze azt a program.

banán
OK
dinnye
Nincs raktáron.
alma
OK

Végösszeg: 180

Második feladat

Adott ugyanez a két tároló. Mennyit ér a raktárkészlet, azaz mennyi pénzt keresne a zöldséges, ha eladná az összeset?

6. DogaDict

A zheredmeny.txt egy képzeletbeli évfolyam NEPTUN kódjait és dolgozatok eredményeit tárolja. Minden sor egy dolgozat adatait tárolja: NEPTUN kód és pontszám, kettősponttal elválasztva.

DHSF7F:24
ZPD9PY:17
LGNUKG:3
XMOO5D:12
...

Tudjuk, hogy a NEPTUN kódok egyediek, ezért egy szótárban (dict típusban) kulcsnak használhatóak.

  • Írj függvényt, amelyik beolvassa egy fájlt, és létrehoz egy szótárat. A szótár kulcsai a NEPTUN kódok, értékei pedig a dolgozat pontszámok lesznek.
  • Hányan írták meg a dolgozatot? Írd ki a programban a dict ismeretében!
  • Készíts statisztikát, hány pontos dolgozatból hány darab született! Vedd észre, hogy ehhez nem kell a ismerni a minimális és a maximális pontszámot. A szótár „lyukas listának” használható, amiben csak bizonyos indexek léteznek. Ellenőrzésképp néhány adat:
    0: 1
    7: 3
    25: 2
    
  • Készíts egy másik szótárat is, amely szintén pontszámmal indexelhető, de most nem létszámot, hanem listát tartalmaz! Pl. kiknek[7] azt fogja tárolni, hogy kik azok (mi a NEPTUN kódjuk azoknak), akik 7 pontos dolgozatot írtak. Ellenőrzésképp pár adat:
    0: ['E3YX24']
    7: ['OI0IO1', 'UPAXFK', 'NDAI3P']
    25: ['IP1WBY', 'K8TOO0']
    

Írj függvényt, amelyik pontszámok szerint növekvő sorban listáz egy ilyen szótárat! Mivel a szótár elemei össze-vissza tárolódnak, ezért rendezni kell őket. Kulcsok szerint iterálással, sorted(d.keys()), vagy sorted(d.items()).

Megoldás
def beolvas(fajlnev):
    d = {}
    with open(fajlnev, "rt") as f:
        for s in f:
            neptun, pont = s.split(":")
            d[neptun] = int(pont)
    return d


def hisztogram(d):
    """Pontszámok szerinti hisztogram."""
    # Az eredeti szótár értékei a pontszámok.
    # Beszúráskor lehet épp új elem jön létre:
    # ezt itt a .get() függvénnyel oldjuk meg.
    # Az if pont in p esetszétválasztás is jó lenne.
    p = {}
    for pont in d.values():
        p[pont] = p.get(pont, 0) + 1
    return p


def kiknek(d):
    """Pontszámok szerinti NEPTUN kód listák."""
    # Itt látni kell a pontokat és a NEPTUN kódokat
    # is, úgyhogy d.items()-en iterál a ciklus.
    # Ha az adott pontszámot még nem láttuk,
    # új listát kell létrehozni.
    n = {}
    for neptun, pont in d.items():
        if pont not in n:
            n[pont] = []
        n[pont].append(neptun)
    return n


def print_dict(d):
    """Kulcsok szerint növekvő sorban listáz."""
    for key, val in sorted(d.items()):
        print("{}: {}".format(key, val))


def main():
    d = beolvas("zheredmeny.txt")   # { neptun: pont }
    print("Létszám:", len(d))
    h = hisztogram(d)               # { pont: létszám }
    print_dict(h)
    n = kiknek(d)                   # { pont: [neptun, neptun, ...] }
    print_dict(n)


main()

7. DogeDict

Egy kutyatenyésztő számára kell programot írnod, amely a kutyákat tartja nyilván. Egy kutyáról a következő adatokat kell megjegyezni: 1) ID, vagyis azonosító, egész szám, 2) név, 3) papa és 4) mama, a kutya szüleinek azonosítói, vagy -1 értékek, ha ismeretlenek.

Mivel egy kutyát mindig ID alapján kell megtalálni, ezért tárolhatod őket egy dict-ben. Pl. az 5-ös azonosítójú kutya a doges[5] lesz:

doges = {}
doges[5] = Doge(5, "Woof")
doges[7] = Doge(7, "Floof")

Így bár úgy viselkedik, mint egy lista, de az indexek akármilyen egész számok lehetnek.

Az alábbi részfeladatok megvalósítása után mindig teszteld a megírt programrészeket!

  • Definiáld a Doge osztályt!
  • Írj függvényt, amelyik kiírja az összes tárolt kutya nevét és azonosítóját!
  • Írj függvényt, amely egy a paraméterként kapott tárolóban megkeresi a szintén paraméterként kapott azonosítójú kutyát! A visszatérési érték a megtalált elem, vagy None. Teszteld ezt olyan módon, hogy a felhasználótól kérsz azonosítókat!
  • Írj függvényt, amely egy megadott azonosítójú kutyát töröl a tárolóból! Figyelj arra, hogy ilyenkor át kell vizsgálni az adatbázist: ha a törölt listaelemet valamelyik másik elem szülőként (papa, mama) hivatkozza, akkor azoknál -1-et kell beírni.
  • Írj függvényt, amely a paraméterként kapott nevű szöveges fájlba kiírja a kutyák adatait, soronként id, név, papa id, mama id formában, szóközökkel elválasztva. Ha ismeretlenek a szülők, az azonosítók helyére a fájlban is -1 kell kerüljön.
  • Írj függvényt, amely visszaolvas egy ilyen fájlt, betölti onnan a kutyák adatait!