Lær Scala fra 0–60: Det grunnleggende

Scala er et generelt programmeringsspråk på høyt nivå som gir en balanse mellom å utvikle funksjonelle og objektorienterte programmer.

Hva handler funksjonell programmering om? Enkelt sagt er funksjoner førsteklasses borgere i funksjonell programmering. For å utvide et kjernesett av funksjoner i et program, har vi en tendens til å skrive flere klasser som strekker seg over visse retningslinjer / grensesnitt. I funksjonell programmering hjelper funksjoner oss med å oppnå det samme.

Vi bruker Scala REPL for alle forklaringer. Det er et veldig nyttig og informativt verktøy for å lære Scala. Den logger søte små meldinger om hvordan koden vår blir tolket og utført.

La oss starte med det grunnleggende først.

1. Variabler

Vi kan definere uforanderlige variabler ved hjelp av val:

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

Muterbare variabler kan defineres og modifiseres ved hjelp av var:

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

Vi bruker deftil å tilordne en etikett til en uforanderlig verdi hvis evaluering blir utsatt til et senere tidspunkt. Det betyr at etikettens verdi blir lat evaluert hver gang etter bruk.

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

Så du noe interessant?

Mens vi definerte alias, ble det ikke tildelt noen verdi alias: Stringsiden den er lat assosiert når vi påkaller den. Hva ville skje hvis vi endrer verdien på name?

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

2. Kontroll flyt

Vi bruker uttalelser om kontrollflyt for å uttrykke beslutningslogikken vår.

Du kan skrive en if-elseuttalelse som nedenfor:

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

Eller du kan bruke while:

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

3. Samlinger

Scala skiller eksplisitt mellom uforanderlige versus foranderlige samlinger - rett fra selve pakkenavnområdet ( scala.collection.immutableeller scala.collection.mutable).

I motsetning til uforanderlige samlinger kan foranderlige samlinger oppdateres eller utvides på plass. Dette gjør at vi kan endre, legge til eller fjerne elementer som en bivirkning.

Men å utføre tillegg, fjerning eller oppdatering av uforanderlige samlinger returnerer en ny samling i stedet.

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

Imidlertid, for å bruke mutable samlinger, må du eksplisitt importere scala.collection.mutable.List.

I ånden av funksjonell programmering vil vi først og fremst basere eksemplene våre på uforanderlige aspekter av språket, med mindre omveier inn i den foranderlige siden.

Liste

Vi kan lage en liste på forskjellige måter:

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

En annen praktisk tilnærming er å definere en liste ved hjelp av ulempeoperatøren ::. Dette forbinder et hodeelement med den gjenværende halen på en liste.

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

Som tilsvarer:

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

Vi kan få tilgang til listeelementer direkte ved indeksen. Husk Scala bruker nullbasert indeksering:

scala> name(2)
res7: String = Mordred

Noen vanlige hjelpermetoder inkluderer:

list.head, som returnerer det første elementet:

scala> name.head
res8: String = Arthur

list.tail, som returnerer halen på en liste (som inkluderer alt unntatt hodet):

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

Sett

Settillater oss å opprette en ikke-gjentatt gruppe av enheter. Listeliminerer ikke duplikater som standard.

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

Her gjentas 'Arthur' to ganger, og det samme er 'Uther'.

La oss lage et sett med samme navn. Legg merke til hvordan det utelukker duplikatene.

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

Vi kan sjekke for eksistensen av spesifikt element i sett ved hjelp av contains():

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

Vi kan legge til elementer i et sett ved hjelp av + -metoden (som tar varargsf.eks. Argumenter med variabel lengde)

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

På samme måte kan vi fjerne elementer ved hjelp av -metoden

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

Kart

Maper en iterabel samling som inneholder kartlegginger fra keyelementer til respektive valueelementer, som kan opprettes 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)

Verdier for en bestemt nøkkel i kartet kan nås som:

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

Vi kan legge til en oppføring i Map ved hjelp av +metoden:

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

For å endre en eksisterende kartlegging legger vi bare til den oppdaterte nøkkelverdien på nytt:

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

Merk at siden samlingen er uforanderlig, returnerer hver redigeringsoperasjon en ny samling ( res0, res1) med endringene som er brukt. Den originale samlingen kingSpousesforblir uendret.

4. Funksjonelle kombinatorer

Nå som vi har lært hvordan vi grupperer et sett av enheter sammen, la oss se hvordan vi kan bruke funksjonelle kombinatorer til å generere meningsfulle transformasjoner på slike samlinger.

I John Hughes 'enkle ord:

En kombinator er en funksjon som 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
  • Noen grunnleggende om ulike samlinger
  • Oversikt over bruk av funksjonelle kombinatorer på samlinger

Jeg håper du fant denne artikkelen nyttig. Det er først i en serie artikler å følge om å lære Scala.

I del to vil vi lære om å definere klasser, egenskaper, innkapsling og andre objektorienterte konsepter.

Gi meg gjerne beskjed om tilbakemeldinger og forslag til hvordan jeg kan forbedre innholdet. Inntil da, ❤ koding.