
Scala ist eine universelle Programmiersprache auf hoher Ebene, die ein Gleichgewicht zwischen der Entwicklung funktionaler und objektorientierter Programme bietet.
Worum geht es bei der funktionalen Programmierung? In einfachen Worten, Funktionen sind die erstklassigen Bürger in der funktionalen Programmierung. Um einen Kernsatz von Funktionen eines Programms zu erweitern, neigen wir dazu, zusätzliche Klassen zu schreiben, die sich auf bestimmte Richtlinien / Schnittstellen erstrecken. In der funktionalen Programmierung helfen uns Funktionen, dasselbe zu erreichen.
Wir werden die Scala REPL für alle Erklärungen verwenden. Es ist ein sehr praktisches und informatives Werkzeug zum Erlernen von Scala. Es protokolliert niedliche kleine Nachrichten darüber, wie unser Code interpretiert und ausgeführt wird.
Beginnen wir zuerst mit den Grundlagen.
1. Variablen
Wir können unveränderliche Variablen definieren mit val
:
scala> val name = "King"name: String = King
Veränderbare Variablen können definiert und modifiziert werden mit var
:
scala> var name = "King"name: String = King
scala> name = "Arthur"name: String = Arthur
Wir verwenden def
, um einem unveränderlichen Wert ein Etikett zuzuweisen, dessen Bewertung für einen späteren Zeitpunkt verschoben wird. Dies bedeutet, dass der Wert des Etiketts bei jeder Verwendung träge bewertet wird.
scala> var name = "King"name: String = King
scala> def alias = namealias: String
scala> aliasres2: String = King
Hast du etwas Interessantes beobachtet?
Beim Definieren alias
wurde kein Wert zugewiesen, alias: String
da er beim Aufrufen träge zugeordnet wird. Was würde passieren, wenn wir den Wert von ändern name
?
scala> aliasres5: String = King
scala> name = "Arthur, King Arthur"name: String = Arthur, King Arthur
scala> aliasres6: String = Arthur, King Arthur
2. Kontrollieren Sie den Fluss
Wir verwenden Kontrollflussanweisungen, um unsere Entscheidungslogik auszudrücken.
Sie können eine if-else
Erklärung wie folgt schreiben :
if(name.contains("Arthur")) { print("Entombed sword")} else { print("You're not entitled to this sword")}
Oder Sie können verwenden while
:
var attempts = 0while (attempts < 3) { drawSword() attempts += 1}
3. Sammlungen
Scala unterscheidet explizit zwischen unveränderlichen und veränderlichen Sammlungen - direkt vom Paket-Namespace selbst ( scala.collection.immutable
oder scala.collection.mutable
).
Im Gegensatz zu unveränderlichen Sammlungen können veränderbare Sammlungen an Ort und Stelle aktualisiert oder erweitert werden. Dies ermöglicht es uns, Elemente als Nebeneffekt zu ändern, hinzuzufügen oder zu entfernen.
Durch Ausführen von Hinzufügungs-, Entfernungs- oder Aktualisierungsvorgängen für unveränderliche Sammlungen wird stattdessen eine neue Sammlung zurückgegeben.
Unveränderliche Sammlungen werden immer automatisch über das importiert scala._
(das auch einen Alias für enthält scala.collection.immutable.List
).
Um veränderbare Sammlungen zu verwenden, müssen Sie jedoch explizit importieren scala.collection.mutable.List
.
Im Sinne der funktionalen Programmierung werden wir unsere Beispiele in erster Linie auf unveränderliche Aspekte der Sprache stützen, mit kleinen Umwegen in die veränderliche Seite.
Aufführen
Wir können eine Liste auf verschiedene Arten erstellen:
scala> val names = List("Arthur", "Uther", "Mordred", "Vortigern")
names: List[String] = List(Arthur, Uther, Mordred, Vortigern)
Ein weiterer praktischer Ansatz besteht darin, eine Liste mit dem ::
Operator cons zu definieren . Dies verbindet ein Kopfelement mit dem verbleibenden Ende einer Liste.
scala> val name = "Arthur" :: "Uther" :: "Mordred" :: "Vortigern" :: Nil
name: List[String] = List(Arthur, Uther, Mordred, Vortigern)
Welches ist gleichbedeutend mit:
scala> val name = "Arthur" :: ("Uther" :: ("Mordred" :: ("Vortigern" :: Nil)))
name: List[String] = List(Arthur, Uther, Mordred, Vortigern)
Wir können direkt über ihren Index auf Listenelemente zugreifen. Denken Sie daran, dass Scala eine auf Null basierende Indizierung verwendet:
scala> name(2)
res7: String = Mordred
Einige gängige Hilfsmethoden sind:
list.head
, das das erste Element zurückgibt:
scala> name.head
res8: String = Arthur
list.tail
, der das Ende einer Liste zurückgibt (die alles außer dem Kopf enthält):
scala> name.tail
res9: List[String] = List(Uther, Mordred, Vortigern)
einstellen
Set
ermöglicht es uns, eine nicht wiederholte Gruppe von Entitäten zu erstellen. List
Duplikate werden standardmäßig nicht entfernt.
scala> val nameswithDuplicates = List("Arthur", "Uther", "Mordred", "Vortigern", "Arthur", "Uther")
nameswithDuplicates: List[String] = List(Arthur, Uther, Mordred, Vortigern, Arthur, Uther)
Hier wird 'Arthur' zweimal wiederholt, ebenso wie 'Uther'.
Lassen Sie uns ein Set mit den gleichen Namen erstellen. Beachten Sie, wie die Duplikate ausgeschlossen werden.
scala> val uniqueNames = Set("Arthur", "Uther", "Mordred", "Vortigern", "Arthur", "Uther")
uniqueNames: scala.collection.immutable.Set[String] = Set(Arthur, Uther, Mordred, Vortigern)
Wir können die Existenz eines bestimmten Elements in Set überprüfen, indem wir contains()
:
scala> uniqueNames.contains("Vortigern")res0: Boolean = true
Wir können einem Set Elemente mit der Methode + hinzufügen (die varargs
Argumente mit variabler Länge verwendet).
scala> uniqueNames + ("Igraine", "Elsa", "Guenevere")res0: scala.collection.immutable.Set[String] = Set(Arthur, Elsa, Vortigern, Guenevere, Mordred, Igraine, Uther)
Ebenso können wir Elemente mit der -
Methode entfernen
scala> uniqueNames - "Elsa"
res1: scala.collection.immutable.Set[String] = Set(Arthur, Uther, Mordred, Vortigern)
Karte
Map
ist eine iterierbare Sammlung, die Zuordnungen von key
Elementen zu entsprechenden value
Elementen enthält, die wie folgt erstellt werden können:
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)
Auf Werte für einen bestimmten Schlüssel in der Karte kann wie folgt zugegriffen werden:
scala> kingSpouses("Vortigern")res0: String = Elsa
Wir können Map einen Eintrag hinzufügen, indem wir folgende +
Methode verwenden:
scala> kingSpouses + ("Launcelot" -> "Elaine")res0: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere, Launcelot -> Elaine)
Um eine vorhandene Zuordnung zu ändern, fügen wir einfach den aktualisierten Schlüsselwert erneut hinzu:
scala> kingSpouses + ("Launcelot" -> "Guenevere")res1: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere, Launcelot -> Guenevere)
Da die Sammlung unveränderlich ist, gibt jeder Bearbeitungsvorgang eine neue Sammlung ( res0
, res1
) mit den angewendeten Änderungen zurück. Die ursprüngliche Sammlung kingSpouses
bleibt unverändert.
4. Funktionskombinatoren
Nachdem wir nun gelernt haben, eine Reihe von Entitäten zu gruppieren, wollen wir sehen, wie wir mithilfe funktionaler Kombinatoren sinnvolle Transformationen für solche Sammlungen generieren können.
In John Hughes 'einfachen Worten:
Ein Kombinator ist eine Funktion, die Programmfragmente aus Programmfragmenten erstellt.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.