
A Scala egy általános célú, magas szintű programozási nyelv, amely egyensúlyt kínál a funkcionális és az objektum-orientált programok fejlesztése között.
Miről szól a funkcionális programozás? Egyszerűen fogalmazva: a függvények a funkcionális programozás első osztályú polgárai. Annak érdekében, hogy kibővítsük a program alapvető funkcióit, hajlamosak vagyunk további osztályokat írni, amelyek bizonyos irányelveken / interfészeken alapulnak. A funkcionális programozásban a függvények segítenek elérni ugyanezt.
Minden magyarázathoz a Scala REPL szolgáltatást használjuk. Nagyon hasznos és informatív eszköz a Scala elsajátításához. Aranyos kis üzeneteket naplóz a kódunk értelmezéséről és végrehajtásáról.
Kezdjük először az alapokkal.
1. Változók
Meghatározhatunk változhatatlan változókat a következők használatával val
:
scala> val name = "King"name: String = King
A változtatható változók meghatározhatók és módosíthatók var
:
scala> var name = "King"name: String = King
scala> name = "Arthur"name: String = Arthur
Az általunk használt def
rendelni egy címkét megváltoztathatatlan érték, amelynek értékelése elhalasztják egy későbbi időpontban. Ez azt jelenti, hogy a címke értékét lustán értékelik minden használatkor.
scala> var name = "King"name: String = King
scala> def alias = namealias: String
scala> aliasres2: String = King
Megfigyeltél valami érdekeset?
A meghatározás alias
során nem rendeltek értéket, alias: String
mivel az lustán társul, amikor hivatkozunk rá. Mi történne, ha megváltoztatnánk az értékét name
?
scala> aliasres5: String = King
scala> name = "Arthur, King Arthur"name: String = Arthur, King Arthur
scala> aliasres6: String = Arthur, King Arthur
2. Ellenőrizze az áramlást
Vezérlési folyamat utasításokat használunk a döntési logikánk kifejezésére.
if-else
Nyilatkozatot írhat az alábbiak szerint:
if(name.contains("Arthur")) { print("Entombed sword")} else { print("You're not entitled to this sword")}
Vagy használhatja while
:
var attempts = 0while (attempts < 3) { drawSword() attempts += 1}
3. Gyűjtemények
A Scala kifejezetten megkülönbözteti a megváltoztathatatlan és a változtatható gyűjteményeket - közvetlenül a csomag névterétől ( scala.collection.immutable
vagy scala.collection.mutable
).
A megváltoztathatatlan gyűjteményektől eltérően a változtatható gyűjtemények a helyükön frissíthetők vagy bővíthetők. Ez lehetővé teszi számunkra, hogy mellékhatásként elemeket változtassunk, adjunk hozzá vagy távolítsunk el.
De a hozzáadási, eltávolítási vagy frissítési műveletek megváltoztathatatlan gyűjtemények esetén egy új gyűjteményt ad vissza.
Változatlan gyűjtemények mindig automatikusan importálhatók az scala._
(amely szintén tartalmaz álnevet scala.collection.immutable.List
).
A mutábilis gyűjtemények használatához azonban kifejezetten importálnia kell scala.collection.mutable.List
.
A funkcionális programozás szellemében példáinkat elsősorban a nyelv változhatatlan aspektusaira alapozzuk, kisebb kitérőkkel a mutálható oldalra.
Lista
Különféle módon hozhatunk létre listát:
scala> val names = List("Arthur", "Uther", "Mordred", "Vortigern")
names: List[String] = List(Arthur, Uther, Mordred, Vortigern)
Egy másik praktikus megközelítés egy lista meghatározása a hátrányok ::
operátor segítségével. Ez összekapcsol egy fej elemet a lista fennmaradó farkaival.
scala> val name = "Arthur" :: "Uther" :: "Mordred" :: "Vortigern" :: Nil
name: List[String] = List(Arthur, Uther, Mordred, Vortigern)
Ami egyenértékű:
scala> val name = "Arthur" :: ("Uther" :: ("Mordred" :: ("Vortigern" :: Nil)))
name: List[String] = List(Arthur, Uther, Mordred, Vortigern)
A listaelemekhez közvetlenül az indexük alapján férhetünk hozzá. Ne feledje, hogy a Scala nulla alapú indexelést használ:
scala> name(2)
res7: String = Mordred
Néhány általános segítő módszer a következőket tartalmazza:
list.head
, amely az első elemet adja vissza:
scala> name.head
res8: String = Arthur
list.tail
, amely egy lista farkát adja vissza (amely a fej kivételével mindent tartalmaz):
scala> name.tail
res9: List[String] = List(Uther, Mordred, Vortigern)
Készlet
Set
lehetővé teszi, hogy nem ismétlődő entitáscsoportot hozzunk létre. List
alapértelmezés szerint nem szünteti meg a duplikátumokat.
scala> val nameswithDuplicates = List("Arthur", "Uther", "Mordred", "Vortigern", "Arthur", "Uther")
nameswithDuplicates: List[String] = List(Arthur, Uther, Mordred, Vortigern, Arthur, Uther)
Itt kétszer megismétlik az „Arthurt”, és az „Uthert” is.
Hozzunk létre egy azonos nevű halmazot. Figyelje meg, hogyan zárja ki az ismétlődéseket.
scala> val uniqueNames = Set("Arthur", "Uther", "Mordred", "Vortigern", "Arthur", "Uther")
uniqueNames: scala.collection.immutable.Set[String] = Set(Arthur, Uther, Mordred, Vortigern)
A Set segítségével ellenőrizhetjük, hogy létezik-e egy adott elem contains()
:
scala> uniqueNames.contains("Vortigern")res0: Boolean = true
A + metódus segítségével elemeket adhatunk egy halmazhoz (amely varargs
változó hosszúságú argumentumokat vesz fel )
scala> uniqueNames + ("Igraine", "Elsa", "Guenevere")res0: scala.collection.immutable.Set[String] = Set(Arthur, Elsa, Vortigern, Guenevere, Mordred, Igraine, Uther)
Hasonlóképpen eltávolíthatunk elemeket a -
módszerrel
scala> uniqueNames - "Elsa"
res1: scala.collection.immutable.Set[String] = Set(Arthur, Uther, Mordred, Vortigern)
Térkép
Map
egy iterálható gyűjtemény, amely leképezéseket tartalmaz az key
elemektől a megfelelő elemekig value
, amelyek így hozhatók létre:
scala> val kingSpouses = Map( | "King Uther" -> "Igraine", | "Vortigern" -> "Elsa", | "King Arthur" -> "Guenevere" | )
kingSpouses: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere)
A térkép egy adott kulcsának értékei a következőképpen érhetők el:
scala> kingSpouses("Vortigern")res0: String = Elsa
Hozzáadhatunk egy bejegyzést a Térképhez a következő +
módszerrel:
scala> kingSpouses + ("Launcelot" -> "Elaine")res0: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere, Launcelot -> Elaine)
Meglévő leképezés módosításához egyszerűen hozzá kell adnunk a frissített kulcsértéket:
scala> kingSpouses + ("Launcelot" -> "Guenevere")res1: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere, Launcelot -> Guenevere)
Vegye figyelembe, hogy mivel a gyűjtemény megváltoztathatatlan, minden szerkesztési művelet egy új gyűjteményt ( res0
, res1
) ad vissza az alkalmazott módosításokkal. Az eredeti gyűjtemény kingSpouses
változatlan marad.
4. Funkcionális kombinátorok
Most, hogy megtanultuk az entitások halmazának csoportosítását, nézzük meg, hogyan használhatjuk a funkcionális kombinátorokat értelmes átalakítások létrehozására az ilyen gyűjteményekben.
John Hughes egyszerű szavaival:
A kombinátor egy olyan funkció, amely programrészleteket épít programrészletekből.An in-depth look at how combinators work is outside of this article’s scope. But, we’ll try to touch upon a high-level understanding of the concept anyhow.
Let’s take an example.
Suppose we want to find names of all queens using the kingSpouses
collection map that we created.
We’d want to do something along the lines of examining each entry in the map. If the key
has the name of a king, then we’re interested in the name of it’s spouse (i.e. queen).
We shall use the filter
combinator on map, which has a signature like:
collection.filter( /* a filter condition method which returns true on matching map entries */)
Overall we shall perform the following steps to find queens:
- Find the (key, value) pairs with kings’ names as keys.
- Extract the values (names of queen) only for such tuples.
The filter
is a function which, when given a (key, value), returns true / false.
- Find the map entries pertaining to kings.
Let’s define our filtering predicate function. Since key_value
is a tuple of (key, value), we extract the key using ._1
(and guess what ._2
returns?)
scala> def isKingly(key_value: (String, String)): Boolean = key_value._1.toLowerCase.contains("king")
isKingly: (key_value: (String, String))Boolean
Now we shall use the filter function defined above to filter
kingly entries.
scala> val kingsAndQueens = kingSpouses.filter(isKingly)
kingsAndQueens: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, King Arthur -> Guenevere)
2. Extract the names of respective queens from the filtered tuples.
scala> kingsAndQueens.values
res10: Iterable[String] = MapLike.DefaultValuesIterable(Igraine, Guenevere)
Let’s print out the names of queens using the foreach
combinator:
scala> kingsAndQueens.values.foreach(println)IgraineGuenevere
Some other useful combinators are foreach
, filter
, zip
, partition
, find
.
We shall re-visit some of these after having learnt how to define functions and passing functions as arguments to other functions in higher-order functions.
Let’s recap on what we’ve learned:
- Different ways of defining variables
- Various control-flow statements
- Some basics about various collections
- Overview of using functional combinators on collections
I hope you found this article useful. It is first in a series of articles to follow on learning Scala.
In part two, we’ll learn about defining classes, traits, encapsulation and other object-oriented concepts.
Please feel free to let me know your feedback and suggestions on how I can improve the content. Until then, ❤ coding.