Tuesday, February 25, 2020

14 - az immutable generic List osztály

Természetesen a lista mint adatszerkezet annyira alap komponense a funkcionális programozás eszköztárának, hogy van belőle built-in, nem kell újra írnunk mindig egyet. Nagyon sokrétű, a funkcionalitásai egy részével össze fogunk ismerkedni a félév során, mindenesetre:
  • A típus neve $\mathrm{List}$ (teljes neve $\mathrm{scala.collection.immutable.List}$, de a Scala $\mathrm{Predef}$ objektumában szerepel mint type alias, ezért látszik a rövid neve is.
  • Generic típus, a lista elemeinek típusát kapja paraméterként. Pl. az eddigi $(1,4,2,8,5,7)$ példánk $\mathrm{List}[\mathrm{Int}]$ típusúnak felel meg.
  • Az eddigi $\mathrm{Ures}$ objektumunknak (amit genericként egyelőre csak mint case classt tudtunk összehozni) megfelelő üres lista a $\mathrm{Nil}$ nevű objektum. Igen, objektum, és mindenféle típusú generic listának megfelel, ezt később jobban meg fogjuk érteni.
  • Az eddigi $\mathrm{Nemures}$ lista objektumunknak megfelelő osztály neve $\mathrm{::}$. Két kettőspont. Tehát akár így is létrehozhatunk egy $(1,4,2)$ értéket a mostani tudásunk alapján:
    
    val list: List[Int] = ::(1,::(4,::(2,Nil)))
    
  • A $\mathrm{::}$ osztály nevét a fenti prefix (avagy lengyel) jelölés helyett infix is használhatjuk konstruáláskor, így szokás (és figyeljük meg, hogy ennek is ki tudja következtetni a type inference, hogy $\mathrm{List}[\mathrm{Int}]$ lesz:
    
    val list = 1 :: 4 :: 2 :: Nil
    
  • A companion object apply metódusának köszönhetően így is létrehozhatjuk ugyanezt:
    
    val list = List(1,4,2,8,5,7)
    
  • A $\mathrm{List}[T]$-ben egyebek mellett szerepelnek a $\mathrm{filter}(p:T\Rightarrow\mathrm{Boolean}):\mathrm{List}[T]$ és a $\mathrm{map}[U](f:T\Rightarrow U):\mathrm{List}[U]$ függvények, pontosan ugyanazzal a viselkedéssel, mint amit az előző posztokban láttunk.
  • A $\mathrm{toString}$ metódusa visszaadja a $\mathrm{List}$ szót, majd zárójelek közé zárva vesszővel elválasztva a lista elemeit.
  • Lehet rá mintailleszteni, az elv ugyanúgy megy, mint a saját korábbi implementációnkban: a nemüres listának van egy feje és egy farka, $\mathrm{head :: tail}$-ként is illeszthető rá a minta, így szoktuk kiírni, de persze fordul a $\mathrm{::(head,tail)}$ minta is.
  • Egy hasznos metódusa a $\mathrm{zip}$, ami egy másik listával "pároztatja össze" úgy, hogy egy $\mathrm{List}[T]$ zipje egy $\mathrm{List}[U]$ zipjével egy $(T,U)$ párokat tartalmazó lista, vagyis egy $\mathrm{List}[(T,U)]$ lesz: az első elemek párja fogja alkotni a zipelt lista első elemét, a másodikoké a másodikat, stb, azaz pl. $\mathrm{List}(1,2,3)\mathrm{.zip~List}(\textrm{"dinnye"},\textrm{"szilva"},\textrm{"narancs"})$ értéke $\mathrm{List}((1,\textrm{"dinnye"}),(2,\textrm{"szilva"}),(3,\textrm{"narancs"}))$. Az ilyen pároknak, melyekből az eredménylista áll, egyébként az első mezőjét a $\_1$, a második mezőjét a $\_2$ adattagjukkal érjük el. Ha a zipelt listák hossza nem azonos, akkor a rövidebb lista hossza lesz az eredmény hossza, a hosszabb lista "leeső" farka nem számít az eredménybe.
  • Amiért a $\mathrm{zip}$ pl. hasznos tud lenni: ha egy $\mathrm{list}$ nemüres listát zipelünk a farkával: $\mathrm{list}.\mathrm{zip}(\mathrm{list.tail})$, az, ha belegondolunk, a szomszédos elemeket teszi egy cellába, pl. $\mathrm{List}(1,4,2,8).\mathrm{zip}(\mathrm{List}(4,2,8))=\mathrm{List}((1,4),(4,2),(2,8))$. Ez hasznos lehet akkor, ha ez alapján akarunk $\mathrm{map}$ni vagy $\mathrm{filter}$ezni.

No comments:

Post a Comment