10 - tuple és case class: a szorzat

A típusos funkcionális programozásban ha szerencsénk van, akkor a nyelvünk erősen típusos (és a Scala az), vagyis mindennek van valamilyen típusa, és kifejezések adott pozícióira csak adott típusú értékek / kifejezések kerülhetnek. Egy ilyen nyelvben vannak beépített típusok, mint Scalában az Int, a Long vagy a String, de vannak eszközök arra is, hogy a programozó saját típust deklaráljon.

Egy ilyet már láttunk: a függvény típust, a következő pedig a tuple, avagy n-es, ami néhány típust párban / tripletben / valahanyasban egyben tárol.

Tuple típus deklarálása egyszerű: csak zárójelbe kell tegyük és vesszővel elválasztva felsorolni az elemeket:

1
2
3
val tuple1: (Int, Int) = (3,4)     //explicit típussal
val tuple2 = (5,6)                 //inferred típussal
val tuple3 = ("Sándor", 42, false) //a típus persze lehet vegyes

Amit tanultunk ebből a kódból:

  • a tupléban lehet egy, kettő, három vagy akár többféle mező
  • a sorrendjük fontos, (3,4) nem ugyanaz, mint (4,3)
  • a koordináták mindegyikének van saját típusa

Például a fenti tuple3 egy (String,Int,Boolean) triple típus, amiről az idea informál is minket hovernél:

triple

Tuple függvényekben

Természetesen semmi gond nincs azzal, hogy egy függvény valamelyik paramétere vagy értékének típusa tuple legyen. Ha például a korábbi for ciklus implementációban nem csak az s int értékét szerettük volna megkapni, hanem pl. az összes ,,változó'' értékét, ami esetleg bármi okból megváltozhatott volna, akkor csak annyi dolgunk lenne, hogy Int helyett (mondjuk) (Int,Int,Int) visszatérési értékűnek deklaráljuk a forLoopot, és adjuk vissza azt az (ebben az esetben) három intet.

A tuple mezői

Ha egy tuplénk jön be, sokszor el akarjuk érni a mezőit, mint ,,első koordináta'', ,,második koordináta'' és társai; erre - hasonlóan ahhoz, mint ahogy C-ben egy struct mezőit érjük el a . (pont) operátoron keresztül van lehetőség, pontosabban egy k-elemű tuplénak van egy _1, egy _2 stb, végül egy _k nevű mezője, melyeken rendre az első, második stb. koordinátákat érjük el:

1
2
val tuple = ("Sándor", 42, false)
println( tuple._2 )               //prints 42

Tuplékkal gyakran fogunk találkozni, mikor majd Mapekkel dolgozunk: egy Mapben kulcs-érték párokat tárolhatunk, más nyelvekben ezt a konstrukciót hívják asszociatív tömbnek vagy dictionarynak is.

case class: tuple on steroids

Egyszerű feladatokra, mikor csak egyben szeretnénk kezelni néhány adatot, egy tuple is megfelelő lehet. Amint ennél egy kicsit több strukturáltságra van szükségünk (a következő posztban látni fogunk ilyet), akár csak annyira, hogy a tuplénk ,,jelent'' valamit - pl. egy (Int,Int)-et váró függvény nem sokat árul el arról, hogy most ide ,,logikailag'' egy intervallum két végpontját kéne behelyettesíteni, vagy egy egész koordinátájú pontot az XY síkon, esetleg egy magasság-tömeg párt. Ilyenkor egyből jobb, ha el is tudjuk nevezni a típusunkat azon felül, hogy megadjuk a mezők típusait. Itt jönnek be a képbe a case classok, amiket ezen a kurzuson továbbra is, amíg csak lehet, immutable struktúraként fogunk kezelni: ahogy egy már létrehozott tuplénak se változtathatjuk meg a mezőit (próbáljuk ki: a tuple._2=43 értékadás nem fog lefordulni), úgy a case class mezői is beállnak konstruáláskor valamire, és az már az objektumunk élettartama alatt végig ugyanaz marad.

Hozzunk létre egy Pont nevű case classt (egyelőre csak gondoljunk úgy a case classra, mint egy structra C-ben), melynek két egész szám koordinátája van, x és y és dolgozzunk vele egy keveset:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
case class Pont(x: Int, y: Int)     //kész, deklaráltuk a típust

//inicializálni többféleképp lehet
val p: Pont = Pont(1,3)      // explicit típusmegadás
val q       = Pont(2,3)      // inferred típusmegadás
val r       = Pont(x=2, y=3) // a mezők neveivel is lehet
val s       = Pont(y=3, x=2) // nem muszáj tartani az eredeti sorrendet

println("p.x = " + p.x + ", p.y = " + p.y) //prints p.x = 1, p.y = 3
println(s)                   // prints Pont(2,3)

Amit tanultunk ebből a kódból:

  • case classt a két kulcsszó megadása, a case class neve, nyitójel, mezők típusokkal felsorolva, csukójel formában lehet, ezzel már deklaráljuk is a típust
  • új példányt készíteni ebből a case classból pedig a fentebbi módokon, ,,függvényhíváshoz hasonlóan'' lehet, akár meg is nevezve a mezőket
  • a mezőkre ugyanúgy tudunk hivatkozni, mint structokéra C-ben: pont, mező neve
  • ha kiíratjuk a case classt, akkor megkapjuk a nevét és a case class deklarációjában szereplő sorrendben a mezőit (ezt nemsokára megértjük, hogy miért)

A case classoknak van még számos előre legyártott tulajdonságuk, melyekről szintén hamarosan szót ejtünk.

A tuplét és a case classt szorzat típusnak is nevezik, azért, mert a lehetséges értékeik halmazát úgy kapjuk, hogy a mezőik lehetséges értékeinek a halmazainak vesszük a direkt (vagy Descartes-) szorzatát. Van összeg típus is, ez a két konstrukció fogja kiadni az úgynevezett algebrai adattípusokat.

Kérdések, feladatok


Utolsó frissítés: 2021-02-07 23:06:47