Lerne Scala von 0 bis 60: Die Grundlagen

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 aliaswurde kein Wert zugewiesen, alias: Stringda 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-elseErklä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.immutableoder 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

Setermöglicht es uns, eine nicht wiederholte Gruppe von Entitäten zu erstellen. ListDuplikate 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 varargsArgumente 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

Mapist eine iterierbare Sammlung, die Zuordnungen von keyElementen zu entsprechenden valueElementen 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 kingSpousesbleibt 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.

  1. 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.