Opi Scala 0–60: Perusteet

Scala on yleiskäyttöinen, korkean tason ohjelmointikieli, joka tarjoaa tasapainon toiminnallisten ja olio-ohjelmien kehittämisen välillä.

Mistä toiminnallinen ohjelmointi on kyse? Yksinkertaisesti sanottuna toiminnot ovat ensiluokkaisia ​​kansalaisia ​​toiminnallisessa ohjelmoinnissa. Ohjelman ydinsarjan laajentamiseksi meillä on tapana kirjoittaa muita luokkia, jotka ulottuvat tiettyihin ohjeisiin / rajapintoihin. Toiminnallisessa ohjelmoinnissa toiminnot auttavat meitä saavuttamaan saman.

Käytämme Scala REPL -ohjelmaa kaikkiin selityksiin. Se on erittäin kätevä ja informatiivinen työkalu Scalan oppimiseen. Se kirjaa söpöt pienet viestit siitä, miten koodimme tulkitaan ja suoritetaan.

Aloitetaan ensin perusasioista.

1. Muuttujat

Voimme määritellä muuttumattomia muuttujia käyttämällä val:

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

Muutettavissa olevat muuttujat voidaan määritellä ja muokata seuraavilla tavoilla var:

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

Käytämme defmäärittää etiketin muuttumattoman arvon, jonka arviointi alkaa vasta myöhemmin. Se tarkoittaa, että etiketin arvo arvioidaan laiskasti joka kerta käytön aikana.

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

Havaitsitko jotain mielenkiintoista?

Määriteltäessä aliasarvoa ei annettu, alias: Stringkoska se liittyy laiskasti, kun käytämme sitä. Mitä tapahtuisi, jos muuttaisimme arvon name?

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

2. Ohjaa virtausta

Ilmaisemme päätöksenteon logiikkaa käyttämällä ohjausvirta-lauseita.

Voit kirjoittaa if-elselausunnon seuraavasti:

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

Tai voit käyttää while:

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

3. Kokoelmat

Scala tekee nimenomaisen eron muuttumattomien ja muuttuvien kokoelmien välillä - suoraan paketin nimiavaruudesta ( scala.collection.immutabletai itse scala.collection.mutable).

Toisin kuin muuttumattomissa kokoelmissa, muutettavissa olevat kokoelmat voidaan päivittää tai laajentaa paikalleen. Tämän avulla voimme muuttaa, lisätä tai poistaa elementtejä sivuvaikutuksena.

Mutta muuttumattomien kokoelmien lisäys-, poisto- tai päivitystoiminnot palauttavat sen sijaan uuden kokoelman.

Muuttamattomat kokoelmat tuodaan aina automaattisesti scala._ (joka sisältää myös aliaksen kohteelle scala.collection.immutable.List) kautta.

Jos haluat käyttää muuttuvia kokoelmia, sinun on kuitenkin tuotava ne nimenomaisesti scala.collection.mutable.List.

Toiminnallisen ohjelmoinnin hengessä perustamme esimerkkimme ensisijaisesti kielen muuttumattomiin osa-alueisiin pienillä kiertoteillä muuttuvalle puolelle.

Lista

Voimme luoda luettelon eri tavoin:

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

Toinen kätevä tapa on määritellä luettelo miinusoperaattorilla ::. Tämä yhdistää pääelementin luettelon jäljellä olevaan hännään.

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

Mikä vastaa:

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

Voimme käyttää luetteloelementtejä suoraan niiden hakemiston perusteella. Muista, että Scala käyttää nollapohjaista indeksointia:

scala> name(2)
res7: String = Mordred

Joitakin yleisiä auttajamenetelmiä ovat:

list.head, joka palauttaa ensimmäisen elementin:

scala> name.head
res8: String = Arthur

list.tail, joka palauttaa luettelon hännän (joka sisältää kaiken paitsi pään):

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

Aseta

Setavulla voimme luoda toistumattomien kokonaisuuksien ryhmän. Listei poista kaksoiskappaleita oletuksena.

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

Tässä 'Arthur' toistetaan kahdesti, samoin 'Uther'.

Luodaan joukko samoilla nimillä. Huomaa, kuinka se sulkee pois kaksoiskappaleet.

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

Voimme tarkistaa tietyn elementin olemassaolon Setissä käyttämällä contains():

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

Voimme lisätä elementtejä sarjaan + -menetelmällä (joka vaatii varargsmuuttuvan pituiset argumentit)

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

Vastaavasti voimme poistaa elementit -menetelmällä

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

Kartta

Mapon toistettavissa oleva kokoelma, joka sisältää kartoituksia keyelementeistä vastaaviin valueelementteihin, jotka voidaan luoda seuraavasti:

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)

Tietyn avaimen arvot kartalla ovat käytettävissä seuraavasti:

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

Voimme lisätä merkinnän Mapiin +menetelmällä:

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

Olemassa olevan kartoituksen muokkaamiseksi yksinkertaisesti lisätään päivitetty avainarvo uudelleen:

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

Huomaa, että koska kokoelma on muuttumaton, kukin muokkaustoiminto palauttaa uuden kokoelman ( res0, res1) muutosten kanssa. Alkuperäinen kokoelma kingSpousespysyy muuttumattomana.

4. Toiminnalliset yhdistimet

Nyt kun olemme oppineet ryhmittelemään joukon entiteettejä, katsotaanpa, kuinka voimme käyttää toiminnallisia yhdistimiä tuottamaan mielekkäitä muutoksia tällaisiin kokoelmiin.

John Hughesin yksinkertaisilla sanoilla:

Kombinaattori on toiminto, joka rakentaa ohjelman fragmentteja ohjelman fragmenteista.

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
  • Joitakin perusteita eri kokoelmista
  • Yleiskatsaus toiminnallisten yhdistimien käyttöön kokoelmissa

Toivon, että pidit tästä artikkelista hyödyllistä. Se on ensimmäinen artikkelisarjassa, joka seuraa Scalan oppimista.

Toisessa osassa opitaan luokkien, piirteiden, kapseloinnin ja muiden olio-käsitteiden määrittelemisestä.

Kerro minulle palautteestasi ja ehdotuksistani sisällön parantamiseksi. Siihen asti ❤ koodaus.