15 - hash halmaz¶
Ebben a leckében egy funkcionális generikus hashsetet fogunk implementálni. Hogy ne kavarodjon a beépített HashSet
-tel,
a mienk HashHalmaz
lesz.
- Egy
HashHalmaz[T]
objektumT
típusú elemek halmazát tárolja. - A hatékony kereshetőség céljából
hashCode
alapján szortírozza szét a tárolt objektumokat. (Ez a metódusa minden Scala objektumnak létezik,Int
et ad vissza.) - Belső adatszerkezete egy
data: Vector[ Vector[T] ]
adatmező, melynek a méretét most nevezzüksize
-nak. - Ha egy
value: T
objektum hashkódjax
, akkor azx
Int
nek asize
-al való osztási maradéka határozza meg, hogy adata
vektor hanyadik cellájában van a helye az elemnek. (Tehát például hasize=8
ésx=21
, akkordata(5)
-ben a helye, hax=-6
, akkordata(2)
-ben.) - Támogatnunk kell a
contains: T => Boolean
metódust, ami miután kiszámolja, hogy a data vektor hanyadik cellájában kéne legyen az elem, az ott tároltVector[T]
-ben keresi meg az elemet. - Támogatnunk kell a
removed: T => HashHalmaz[T]
metódust, ami visszaad egy új adatszerkezetet, mely ugyanaz, mint az eredeti, azzal a különbséggel, hogy a kért elem ha benne volt, már nincs. - Támogatnunk kell a
inserted: T => HashHalmaz[T]
metódust, ami visszaad egy új adatszerkezetet, mely ugyanaz, mint az eredeti, azzal a különbséggel, hogy a kért elem ha nem volt benne, akkor már igen. - Támogatnunk kell az intelligens átskálázást: ha a
data
vektorban beszúrás után több, mint3/4
-e a celláknak tartalmaz nemüres vektort, akkor azinserted
metódus által visszaadottHashHalmaz
-ban asize
legyen kétszer akkora, mint eredetileg volt (és így persze újra kell osztani az elemeket). - Implementáljuk azt, hogy a
HashHalmaz[T]()
kifejezés egy üres hashhalmazt hozzon létre,size=8
mérettel. - Implementáljuk azt, hogy a
HashHalmaz[T](size: Int)
kifejezés egy üres hashhalmazt hozzon létre,size
mérettel. - Támogassuk a
foreach
,map
,flatMap
,filter
,foldLeft
metódusokat a szokásos fejlécekkel és elvárt viselkedéssel.
osztály, companion object¶
A leírás szerint az osztály lényege a data
adatmező, melyet nem akarunk láthatóvá tenni.
Erre van lehetőség: a case class fejlécében bármelyik paramétert feltüntethetjük private val
ként,
és ekkor létrehozáskor ugyan át kell adnunk ezt a paramétert is, de nem fogják tudni kódból elérni.
Ezek alapján hogy kezdenénk bele az osztály implementálásába?
1 |
|
1 2 3 |
|
Válasz mutatása
Talán a következő könnyű feladat a két kért konstruktor megírása. Hogy tudjuk a kért funkciót támogatni?
Hint mutatása
1 2 3 4 5 6 7 8 9 |
|
Válasz mutatása
getIndex, contains¶
Privát metódusként implementáljuk a getIndexOf: T => Int
függvényt, ami a kapott objektum hashkódja alapján visszaadja,
hogy hanyadik cellába való az elem! Note: Scalában a negatívok maradékos osztásának maradéka negatív, pl.
(-8) % 6 == -2
.
1 2 3 4 |
|
Válasz mutatása
Ezt a metódust használva a contains: T => Boolean
könnyen implementálható. Hogyan?
1 2 |
|
Válasz mutatása
removed¶
Törlésnél - elkerülendő a felesleges másolásokat - ha eleve nem szerepelt az elem a halmazban, adjuk vissza a this
objektumot
változtatás nélkül! Ebben a feladatban talán hasznos metódusok a Vector
osztályban:
updated( pos: Int, value: T ): Vector[T]
visszaad egy új vektort, mely annyiban különbözik az eredetitől, hogypos
pozíción értékevalue
dropRight( n: Int ): Vector[T]
: visszaad egy új vektort, mely az eredetiből úgy készül, hogy jobbról levágn
elemetindexOf( value: T ): Int
visszaadja, hogy a vektornakvalue
hanyadik eleme, vagy-1
-et, ha nincs benne.
Próbáljuk meg a vektorból törlést hatékonyan megvalósítani.
Hint mutatása
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Válasz mutatása
insert, foldLeft¶
Beszúrásnál alapvetően működne a törléshez hasonló megközelítés, kivéve azt a részt, amikor is ki kell bővíteni duplájára a data
vektor méretét.
Ekkor készítenünk kell egy új üres hashhalmazt kétszer akkora méretben és hozzáadni egyesével a meglévő elemeket.
Erre korábban a foldLeft
metódust láttuk, és most is alkalmas lehet rá, ha implementálva lesz. Kezdjük tehát talán azzal!
A foldLeft[U]
metódus kap egy U
típusú kezdőértéket, és visszaad egy függvényt, mely kap egy (U,T)=>U
függvényt, és visszaad egy U
értéket. Próbáljuk meg megírni a függvény fejlécét úgy, hogy leforduljon!
1 |
|
Válasz mutatása
Implementáljuk is a foldLeft
et mondjuk úgy, hogy a kezdeti értékből kiindulva minden, a datában tárolt vektorral szép sorban foldolunk!
Hint mutatása
1 2 3 4 5 6 7 8 9 |
|
Válasz mutatása
Ha már van foldLeft
ünk, írjuk meg az inserted
fejlécét és felhasználva a (még nem megírt) insertedet, implementáljunk
egy ++( that: HashHalmaz[T] ): HashHalmaz[T]
metódust, ami visszaadj a két hashhalmaz unióját!
Hint mutatása
1 2 3 4 |
|
Hint mutatása
Ezeket felhasználva már tudjuk implementálni az inserted
metódust is. Hogyan?
Hint mutatása
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Válasz mutatása
foreach, filter, map, flatMap¶
A foreach
metódus a szokásos fejléccel menjen végig az összes belső vektoron sorban és mindegyikek minden elemén értékelje ki a kapott függvényt!
Hint mutatása
1 2 3 4 5 |
|
Válasz mutatása
A filter( p: T=>Boolean ): HashHalmaz[T]
metódust filterezett foldlefttel implementálhatjuk például, hiszen minden elem ugyanabban a vödörben maradhat, mint volt.
1 2 3 4 5 6 |
|
Válasz mutatása
A map
metódusnál viszont már eleve a típus is lehet más, az értékek hashkódjának sincs köze az eredetiekhez, így egy teljesen új HashHalmazba érdemes
egyesével beszúrni az elemeket -- foldLefttel, amit már úgyis implementáltunk.
1 2 3 4 5 |
|
Válasz mutatása
Végül a flatMap
metódus csak annyiban kell különbözzön a map
től, hogy nem az összes f(eredetiElem)
értéket kell egyesével beszúrnunk,
hanem hozzá kell uniózzuk az eddig legyártott halmazhoz.
1 2 3 4 5 |
|
Válasz mutatása
További feladatként teszteljük a kódunkat!