Lær Scala fra 0–60: Grundlæggende

Scala er et generelt programmeringssprog på højt niveau, der giver en balance mellem udvikling af funktionelle og objektorienterede programmer.

Hvad handler funktionel programmering om? Enkelt sagt er funktioner de førsteklasses borgere i funktionel programmering. For at udvide et kernesæt af funktionaliteter i et program, har vi en tendens til at skrive yderligere klasser, der udvider visse retningslinjer / grænseflader. I funktionel programmering hjælper funktioner os med at opnå det samme.

Vi bruger Scala REPL til alle forklaringer. Det er et meget praktisk og informativt værktøj til at lære Scala. Det logger søde små beskeder om, hvordan vores kode fortolkes og udføres.

Lad os starte med det grundlæggende først.

1. Variabler

Vi kan definere uforanderlige variabler ved hjælp af val:

scala> val name = "King"name: String = King

Mutable variabler kan defineres og modificeres ved hjælp af var:

scala> var name = "King"name: String = King
scala> name = "Arthur"name: String = Arthur

Vi bruger deftil at tildele en etiket til en uforanderlig værdi, hvis evaluering udsættes til et senere tidspunkt. Det betyder, at mærkets værdi vurderes dovent hver gang efter brug.

scala> var name = "King"name: String = King
scala> def alias = namealias: String
scala> aliasres2: String = King

Har du observeret noget interessant?

Mens vi definerede alias, blev der ikke tildelt nogen værdi, alias: Stringda den dovent tilknyttes, når vi påberåber den. Hvad ville der ske, hvis vi ændrer værdien af name?

scala> aliasres5: String = King
scala> name = "Arthur, King Arthur"name: String = Arthur, King Arthur
scala> aliasres6: String = Arthur, King Arthur

2. Styr flow

Vi bruger kontrolflowangivelser til at udtrykke vores beslutningslogik.

Du kan skrive en if-elseerklæring som nedenfor:

if(name.contains("Arthur")) { print("Entombed sword")} else { print("You're not entitled to this sword")}

Eller du kan bruge while:

var attempts = 0while (attempts < 3) { drawSword() attempts += 1}

3. Samlinger

Scala skelner eksplicit mellem uforanderlige versus mutable samlinger - lige fra selve pakkenavneområdet ( scala.collection.immutableeller scala.collection.mutable).

I modsætning til uforanderlige samlinger kan ændrede samlinger opdateres eller udvides på plads. Dette giver os mulighed for at ændre, tilføje eller fjerne elementer som en bivirkning.

Men udførelse af tilføjelse, fjernelse eller opdatering af uforanderlige samlinger returnerer i stedet en ny samling.

Uforanderlige samlinger importeres altid automatisk via scala._ (som også indeholder alias for scala.collection.immutable.List).

For at bruge mutable samlinger skal du dog importere det eksplicit scala.collection.mutable.List.

I ånden med funktionel programmering baserer vi primært vores eksempler på uforanderlige aspekter af sproget med mindre omveje ind i den ændrede side.

Liste

Vi kan oprette en liste på forskellige måder:

scala> val names = List("Arthur", "Uther", "Mordred", "Vortigern")
names: List[String] = List(Arthur, Uther, Mordred, Vortigern)

En anden praktisk tilgang er at definere en liste ved hjælp af ulemper- ::operatøren. Dette forbinder et hovedelement med den resterende hale på en liste.

scala> val name = "Arthur" :: "Uther" :: "Mordred" :: "Vortigern" :: Nil
name: List[String] = List(Arthur, Uther, Mordred, Vortigern)

Hvilket svarer til:

scala> val name = "Arthur" :: ("Uther" :: ("Mordred" :: ("Vortigern" :: Nil)))
name: List[String] = List(Arthur, Uther, Mordred, Vortigern)

We can access list elements directly by their index. Remember Scala uses zero-based indexing:

scala> name(2)
res7: String = Mordred

Some common helper methods include:

list.head, which returns the first element:

scala> name.head
res8: String = Arthur

list.tail, which returns the tail of a list (which includes everything except the head):

scala> name.tail
res9: List[String] = List(Uther, Mordred, Vortigern)

Set

Set allows us to create a non-repeated group of entities. List doesn’t eliminate duplicates by default.

scala> val nameswithDuplicates = List("Arthur", "Uther", "Mordred", "Vortigern", "Arthur", "Uther")
nameswithDuplicates: List[String] = List(Arthur, Uther, Mordred, Vortigern, Arthur, Uther)

Here, ‘Arthur’ is repeated twice, and so is ‘Uther’.

Let’s create a Set with the same names. Notice how it excludes the duplicates.

scala> val uniqueNames = Set("Arthur", "Uther", "Mordred", "Vortigern", "Arthur", "Uther")
uniqueNames: scala.collection.immutable.Set[String] = Set(Arthur, Uther, Mordred, Vortigern)

We can check for the existence of specific element in Set using contains():

scala> uniqueNames.contains("Vortigern")res0: Boolean = true

We can add elements to a Set using the + method (which takes varargs i.e. variable-length arguments)

scala> uniqueNames + ("Igraine", "Elsa", "Guenevere")res0: scala.collection.immutable.Set[String] = Set(Arthur, Elsa, Vortigern, Guenevere, Mordred, Igraine, Uther)

Similarly we can remove elements using the - method

scala> uniqueNames - "Elsa"
res1: scala.collection.immutable.Set[String] = Set(Arthur, Uther, Mordred, Vortigern)

Map

Maper en iterabel samling, der indeholder kortlægninger fra keyelementer til respektive valueelementer, som kan oprettes som:

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)

Værdier for en bestemt nøgle på kortet kan fås som:

scala> kingSpouses("Vortigern")res0: String = Elsa

Vi kan tilføje en post til Map ved hjælp af +metoden:

scala> kingSpouses + ("Launcelot" -> "Elaine")res0: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere, Launcelot -> Elaine)

For at ændre en eksisterende kortlægning tilføjer vi blot den opdaterede nøgleværdi igen:

scala> kingSpouses + ("Launcelot" -> "Guenevere")res1: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere, Launcelot -> Guenevere)

Bemærk, at da samlingen er uforanderlig, returnerer hver redigeringshandling en ny samling ( res0, res1) med de anvendte ændringer. Den originale samling kingSpousesforbliver uændret.

4. Funktionelle kombinatorer

Nu hvor vi har lært at gruppere et sæt enheder sammen, lad os se, hvordan vi kan bruge funktionelle kombinatorer til at generere meningsfulde transformationer på sådanne samlinger.

I John Hughes 'enkle ord:

En kombinator er en funktion, der bygger programfragmenter fra programfragmenter.

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
  • Nogle grundlæggende om forskellige samlinger
  • Oversigt over brug af funktionelle kombinatorer i samlinger

Jeg håber, du fandt denne artikel nyttig. Det er først i en række artikler, der følger om at lære Scala.

I del to lærer vi os om at definere klasser, træk, indkapsling og andre objektorienterede begreber.

Du er velkommen til at fortælle dig din feedback og forslag til, hvordan jeg kan forbedre indholdet. Indtil da ❤ kodning.