Naučite Scala od 0–60: Osnove

Scala je programski jezik visoke razine opće namjene koji nudi ravnotežu između razvoja funkcionalnih i objektno orijentiranih programa.

Što je uopće funkcionalno programiranje? Jednostavno rečeno, funkcije su prvorazredni građani u funkcionalnom programiranju. Kako bismo proširili temeljni skup funkcionalnosti programa, skloni smo pisati dodatne razrede koji se protežu na određene smjernice / sučelja. U funkcionalnom programiranju, funkcije nam pomažu da postignemo isto.

Za sva objašnjenja koristit ćemo Scala REPL. To je vrlo zgodan i informativan alat za učenje Scale. Zapisuje slatke male poruke o tome kako se naš kod tumači i izvršava.

Krenimo prvo s osnovama.

1. Varijable

Nepromjenjive varijable možemo definirati pomoću val:

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

Promjenjive varijable mogu se definirati i modificirati pomoću var:

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

Koristimo defza dodjeljivanje oznake nepromjenjivoj vrijednosti čija se procjena odgađa za kasnije. To znači da se vrijednost naljepnice lijeno procjenjuje svaki put nakon upotrebe.

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

Jeste li promatrali nešto zanimljivo?

Tijekom definiranja aliasnije dodijeljena vrijednost, alias: Stringjer je lijeno povezana, kada je prizivamo. Što bi se dogodilo ako promijenimo vrijednost name?

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

2. Kontrolni tok

Izjave kontrolnog toka koristimo za izražavanje logike odluke.

if-elseIzjavu možete napisati kao u nastavku:

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

Ili možete koristiti while:

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

3. Zbirke

Scala eksplicitno razlikuje nepromjenjive od nepromjenjivih kolekcija - izravno iz samog prostora imena paketa ( scala.collection.immutableili scala.collection.mutable).

Za razliku od nepromjenjivih zbirki, promjenjive zbirke mogu se ažurirati ili proširiti. To nam omogućuje promjenu, dodavanje ili uklanjanje elemenata kao nuspojavu.

Ali izvođenje operacija dodavanja, uklanjanja ili ažuriranja na nepromjenjivim zbirkama umjesto toga vraća novu zbirku.

Nepromjenjive zbirke uvijek se automatski uvoze putem scala._ (koji također sadrži zamjensko ime za scala.collection.immutable.List).

Međutim, da biste koristili izmjenjive zbirke, morate izričito uvesti scala.collection.mutable.List.

U duhu funkcionalnog programiranja, svoje ćemo primjere prvenstveno temeljiti na nepromjenjivim aspektima jezika, s malim zaobilaznim stranama na promjenjivu stranu.

Popis

Popis možemo stvoriti na razne načine:

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

Sljedeći je prikladan pristup definiranje popisa pomoću ::operatora kontra . Ovo spaja element glave s preostalim repom popisa.

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

Što je ekvivalentno:

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

Elementima popisa možemo pristupiti izravno prema njihovom indeksu. Zapamtite da Scala koristi indeksiranje temeljeno na nuli:

scala> name(2)
res7: String = Mordred

Neke uobičajene pomoćne metode uključuju:

list.head, koji vraća prvi element:

scala> name.head
res8: String = Arthur

list.tail, koji vraća rep popisa (koji uključuje sve osim glave):

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

Postavi

Setomogućuje nam stvaranje neponovljene skupine entiteta. Listne uklanja duplikate prema zadanim postavkama.

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

Ovdje se 'Arthur' ponavlja dva puta, pa tako i 'Uther'.

Stvorimo Set s istim imenima. Primijetite kako isključuje duplikate.

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

Možemo li provjeriti postojanje određenog elementa u Setu pomoću contains():

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

Elemente možemo dodati u Set pomoću metode + (koja uzima varargsargumente promjenjive duljine)

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

Na sličan način možemo ukloniti elemente pomoću -metode

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

Karta

Mapje iterabilna zbirka koja sadrži preslikavanja od keyelemenata do odgovarajućih valueelemenata, koji se mogu stvoriti kao:

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)

Vrijednostima za određeni ključ na mapi možete pristupiti kao:

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

Unos u Map možemo dodati +metodom:

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

Da bismo izmijenili postojeće mapiranje, jednostavno ponovno dodamo ažurirani ključ / vrijednost:

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

Imajte na umu da, budući da je zbirka nepromjenjiva, svaka operacija uređivanja vraća novu zbirku ( res0, res1) s primijenjenim promjenama. Izvorna kolekcija kingSpousesostaje nepromijenjena.

4. Funkcionalni kombinatori

Sad kad smo naučili kako grupirati skup entiteta, pogledajmo kako možemo koristiti funkcionalne kombinatore za generiranje značajnih transformacija na takvim zbirkama.

Jednostavnim riječima Johna Hughesa:

Kombinator je funkcija koja gradi programske fragmente iz programskih fragmenata.

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.