19 - foreach és for comprehension¶
A List[T]
osztály rendelkezik még egy szintén generikus foreach
metódussal:
1 |
|
U
paraméterben), kiértékeli az f
függvényt a lista összes elemén,
az eredményeket pedig eldobja. Nagyon hasonlót implementáltunk már, mikor for ciklust írtunk.
- Hogy írnánk meg a
foreach
metódust a saját lista implementációnkon?
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Válasz mutatása
- Hogy íratnánk ki egy
list
lista elemeit szóközzel elválasztva? Az utolsó elem után tehetünk plusz egy spacet.
1 2 3 |
|
Válasz mutatása
Ez azt is jelenti, hogy a paraméterként átadott függvényt a mellékhatása miatt hívjuk meg, hiszen az érték
,,eltűnik'', az eredmény mindenképp ()
lesz, a Unit
típus egyetlen eleme.
string interpoláció, toString¶
Az előző feladat list.foreach( { x => print( x + " " ) } )
mintamegoldásában esetleg szöget üthetett valakinek
a fejébe, hogy ez most miért is fordul le egyáltalán? A list
előtte List[Int]
ként van deklarálva, ezek szerint
lehet Int
et és String
et összeadni, majd azt kiíratni?
-
Egyrészt igen, a https://www.scala-lang.org/api/current/scala/Int.html doksiját megnézve láthatjuk, hogy szerepel az
Int
osztályban a+(x: String): String
metódus, ami ahogy korábban láthattuk, perfectly fine deklaráció, ezért fordul le pl. a7 + "fő"
kifejezés, és lesz az értéke a"7fő"
string. Ugyanakkor, a doksi azt is mondja, hogy 2.13.0 óra ez deprecated, azaz más módszer ajánlott helyette, mégpedig a string interpoláció:- ha egy
""
macskakörmök közti string elé azs
karaktert írjuk, akkor a benne lévő$
jelekkel változókat, sőt tetszőleges kifejezéseket írhatunk - pl. a fenti esetben
s"$x "
rendben van, ez így egy interpolált string, avagy processed string literal - a dollárt ha változónév követi, akkor ennek helyére a változónak az értéke helyettesítődik be
- ha konkrétan a dollár jelre van szükségünk, akkor két dollárt írva megkapjuk a dollár karaktert
- ha a dollár után kapcsosba teszünk egy tetszőleges kifejezést, azt kiértékeli a scala runtime és az eredmény kerül behelyettesítésre.
- ha egy
A string interpoláció is sokkal olvashatóbbá tud tenni egy kódot, érdemes használni, létezik még az f
és a raw
mint beépített interpolátorok az s
mellett, érdemes lehet átfutni a doksiját.
-
Másrészt, a
print(x)
is lefordulna, bármilyen típusú isx
. Ennek oka az, hogy- minden Scala objektumnak (a Javahoz hasonlóan) van egy
toString: String
metódusa - a
print
metódusnak van egyprint( s: String )
változata és egyprint( a: Any )
változata is (azAny
osztályról hamarosan lesz szó) - a
print(a: Any)
metódus pedig mindösszesen annyit csinál, hogy meghívja aprint( a.toString )
-et - korábban mikor a case classainkat kiírattuk, azért néztek ki olyan olvashatóan, mert a case classokhoz a fordító generál egy
megfelelő
override toString
metódust automatikusan, az osztály neve, nyitójel, mezők neve, egyenlő, értékek toStringjei vesszőkkel elválasztva, csukójel formátumban, mint pl. aPont(x=3,y=2)
is volt - persze ha mi is felülírjuk a
toString
et, akkor a fordító nem generál automatikusan, csak ha nem tesszük.
- minden Scala objektumnak (a Javahoz hasonlóan) van egy
for comprehension¶
Ahogy Javában is be lehet járni a kollekciókat elemenként, a Scalás listának is van ún. ,,for comprehension''-je, melyet szintaktikailag így írhatunk fel:
1 2 3 4 5 |
|
for (
, egy azonosító, amivel a lista ,,általános''
elemét fogjuk a magban hivatkozni, példában most x
, majd egy <-
operátor, amit a listánk követ, )
és végül egy
kifejezés, melyben jó, ha szerepel is a listaelem azonosítónk, most az x
.
Ha valaki belenézett a gyakorlatos tesztesetekbe, akkor láthatott benne for( n <- 1 to 100 ) ...
alakú részeket,
ez nagyon hasonlít ehhez a fentihez, és tényleg, a lenti is valid Scala kód:
1 |
|
A for comprehensionnel kapcsolatban az a valóság, hogy ha a Scala fordító egy for ( x <- c ) E
alakú kifejezést talál,
akkor azt automatikusan átalakítja c.foreach( { x => E } )
alakúra. Tehát például ez a két kód teljesen ugyanaz:
1 2 3 |
|
1 to 10
: a Range
osztály¶
Nincs ez másképp a for ( n <- 1 to 10 ) E
alakú kifejezéssel sem. Mire is kellene annak fordulnia ezek szerint?
1 |
|
Válasz mutatása
Erre is fordul! Nem arról van szó, hogy az 1 to 10
valamiféle spéci nyelvi elem lenne. Arról van szó, hogy az Int
osztálynak van egy to(m: Int): Range
metódusa, ami visszaad egy Range
típusú objektumot, melynek van foreach
metódusa, ami az intervallum összes elemén kiértékeli az f
függvényt.
Hogyan implementálnánk egy hasonló osztályt?
1 2 3 4 5 6 7 8 9 10 11 |
|
Válasz mutatása
saját osztályunkban foreach grants for comprehension¶
A foreach
nem egy ad hoc Scala név: a legtöbb programozási nyelvben így hívják a kollekciókat bejáró metódust.
Annak, hogy Scalában a fordító egy for comprehensiont foreach hívássá konvertál, majd az átalakított változatot fordítja
le, az a következménye, hogy a saját osztályainkban is ha deklarálunk egy def foreach[U]( f: T => U ): Unit
metódust,
ahol mondjuk T
is valamiféle generikus típus, akkor használhatunk rajta for comprehensiont for free.
Tehát:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|