Uvod u Scalu

1. Uvod

U ovom uputstvu pogledat ćemo Scalu - jedan od primarnih jezika koji se izvode na Java virtualnom stroju.

Započet ćemo s osnovnim jezičnim značajkama kao što su vrijednosti, varijable, metode i upravljačke strukture. Zatim ćemo istražiti neke napredne značajke kao što su funkcije višeg reda, curry, klase, objekti i podudaranje uzoraka.

Da biste dobili pregled JVM jezika, pogledajte naš brzi vodič za JVM jezike

2. Postavljanje projekta

U ovom uputstvu koristit ćemo standardnu ​​instalaciju Scale s //www.scala-lang.org/download/.

Prvo, dodajmo ovisnost o biblioteci scala u naš pom.xml. Ovaj artefakt pruža standardnu ​​biblioteku za jezik:

 org.scala-lang scala-knjižnica 2.12.7 

Drugo, dodajmo dodatak scala-maven za sastavljanje, testiranje, izvođenje i dokumentiranje koda:

 net.alchim31.maven scala-maven-plugin 3.3.2 kompajlirati testCompile 

Maven ima najnovije artefakte za scala-lang i scala-maven-plugin.

Konačno, koristit ćemo JUnit za jedinstveno testiranje.

3. Osnovne značajke

U ovom ćemo odjeljku kroz primjere ispitati osnovne značajke jezika. U tu ćemo svrhu koristiti tumač Scala.

3.1. Tumač

Tumač je interaktivna ljuska za pisanje programa i izraza.

Isprintajmo "zdravi svijet" koristeći ga:

C: \> scala Dobrodošli u Scala 2.12.6 (Java HotSpot (TM) 64-bitni poslužitelj VM, Java 1.8.0_92). Upišite izraze za ocjenu. Ili pokušajte: pomoć. scala> print ("Pozdrav svijetu!") Pozdrav svijetu! skala>

Iznad započinjemo tumač upisivanjem "scala" u naredbeni redak. Tumač se pokreće i prikazuje poruku dobrodošlice nakon koje slijedi upit.

Zatim upišemo svoj izraz u ovaj upit. Tumač čita izraz, procjenjuje ga i ispisuje rezultat. Zatim se petlja i ponovno prikazuje upit.

Budući da pruža trenutne povratne informacije, prevoditelj je najlakši način da započnete s jezikom. Stoga, upotrijebimo ga da istražimo osnovne jezične značajke: izraze i razne definicije.

3.2. Izrazi

Bilo koji izračunljivi izraz je izraz.

Napišimo neke izraze i pogledajte njihove rezultate:

skala> 123 + 321 res0: Int = 444 skala> 7 * 6 res1: Int = 42 skala> "Zdravo," + "Svijet" res2: String = Zdravo, svjetska skala> "zipZAP" * 3 res3: String = zipZAPzipZAPzipZAP skala > if (11% 2 == 0) "even" else "ak" res4: String = ak

Kao što gore možemo vidjeti, svaki izraz ima vrijednost i vrstu.

Ako izraz nema što vratiti, vraća vrijednost tipa Jedinica. Ova vrsta ima samo jednu vrijednost: (). Sličan je poništiti ključna riječ u Javi.

3.3. Definicija vrijednosti

Ključna riječ val koristi se za deklariranje vrijednosti.

Koristimo ga za imenovanje rezultata izraza:

skala> val pi: Double = 3,14 pi: Double = 3,14 skala> ispis (pi) 3,14 

To nam omogućuje ponovnu upotrebu rezultata više puta.

Vrijednosti su nepromjenjive. Stoga ih ne možemo dodijeliti:

skala> pi = 3,1415: 12: pogreška: prenamjena u val pi = 3,1415 ^

3.4. Definicija varijable

Ako trebamo dodijeliti vrijednost, umjesto toga je deklariramo kao varijablu.

Ključna riječ var koristi se za deklariranje varijabli:

skala> var radijus: Int = 3 radijus: Int = 3

3.5. Definicija metode

Metode definiramo pomoću def ključna riječ. Nakon ključne riječi određujemo naziv metode, deklaracije parametara, separator (dvotačka) i tip povratka. Nakon toga navodimo separator (=) iza kojeg slijedi tijelo metode.

Za razliku od Jave, mi ne koristimo povratak ključna riječ za vraćanje rezultata. Metoda vraća vrijednost zadnjeg procijenjenog izraza.

Napišimo metodu prosj za izračunavanje prosjeka dva broja:

scala> def avg (x: Double, y: Double): Double = {(x + y) / 2} avg: (x: Double, y: Double) Double

Zatim, pozovimo ovu metodu:

skala> prosjek (10,20) res0: Dvostruko = 12,5 

Ako metoda ne uzima nikakve parametre, možemo izostaviti zagrade tijekom definiranja i pozivanja. Dodatno, zagrade možemo izostaviti ako tijelo ima samo jedan izraz.

Napišimo metodu bez parametara bacanje novčića koji slučajno vraća "Glavu" ili "Rep":

scala> def coinToss = if (Math.random> 0,5) "Head" else "Rep" coinToss: String

Dalje, pozovimo se na ovu metodu:

scala> println (coinToss) Stražnja skala> println (coinToss) Glava

4. Kontrolne strukture

Kontrolne strukture omogućuju nam da promijenimo tok kontrole u programu. Imamo sljedeće upravljačke strukture:

  • Izraz if-else
  • Dok petlja i uradi dok petlja
  • Za izražavanje
  • Pokušajte s izrazom
  • Izraz podudaranja

Za razliku od Jave, mi nemamo nastaviti ili pauza ključne riječi. Imamo povratak ključna riječ. Međutim, trebali bismo izbjegavati upotrebu.

Umjesto sklopka izjavu, imamo Pattern Matching putem izraza podudaranja. Uz to, možemo definirati vlastite apstrakcije upravljanja.

4.1. ako-drugo

The ako-drugo izraz je sličan Javi. The drugo dio nije obavezan. Možemo ugnijezditi više izraza if-else.

Budući da je to izraz, on vraća vrijednost. Stoga ga koristimo slično kao i ternarni operator (? :) u Javi. Zapravo jezik nema ternarnog operatora.

Koristeći if-else, napišimo metodu za izračunavanje najvećeg zajedničkog djelitelja:

def gcd (x: Int, y: Int): Int = {if (y == 0) x else gcd (y, x% y)}

Zatim, napišimo jedinični test za ovu metodu:

@Test def whenGcdCalledWith15and27_then3 = {assertEquals (3, gcd (15, 27))}

4.2. Dok je Petlja

Petlja while ima stanje i tijelo. Više puta procjenjuje tijelo u petlji dok je uvjet istinit - stanje se procjenjuje na početku svake iteracije.

Budući da nema ništa korisno za povratak, vraća se Jedinica.

Upotrijebimo while petlju da napišemo metodu za izračunavanje najvećeg zajedničkog djelitelja:

def gcdIter (x: Int, y: Int): Int = {var a = x var b = y while (b> 0) {a = a% b val t = a a = b b = t} a}

Zatim, provjerimo rezultat:

assertEquals (3, gcdIter (15, 27))

4.3. Do While Loop

Petlja do while slična je petlji while, osim što se uvjet petlje procjenjuje na kraju petlje.

Koristeći petlju do-while, napišimo metodu za izračunavanje faktorijela:

def factorial (a: Int): Int = {var rezultat = 1 var i = 1 do {rezultat * = i i = i + 1} dok (i <= a) rezultat}

Dalje, provjerimo rezultat:

assertEquals (720, faktorijel (6))

4.4. Za izražavanje

Izraz for mnogo je svestraniji od for petlje u Javi.

Može ponavljati jednu ili više zbirki. Štoviše, može filtrirati elemente kao i stvarati nove kolekcije.

Koristeći izraz for, napišimo metodu za zbrajanje raspona cijelih brojeva:

def rangeSum (a: Int, b: Int) = {var sum = 0 za (i <- a do b) {sum + = i} sum}

Ovdje, a do b je generatorski izraz. Generira niz vrijednosti iz a do b.

i <- a do b je generator. Ona definira ja kao val i dodjeljuje mu niz vrijednosti proizvedenih izrazom generatora.

Tijelo se izvršava za svaku vrijednost u nizu.

Dalje, provjerimo rezultat:

assertEquals (55, rangeSum (1, 10))

5. Funkcije

Scala je funkcionalan jezik. Funkcije su ovdje prvorazredne vrijednosti - možemo ih koristiti kao i bilo koji drugi tip vrijednosti.

U ovom ćemo odjeljku razmotriti neke napredne koncepte koji se odnose na funkcije - lokalne funkcije, funkcije višeg reda, anonimne funkcije i currying.

5.1. Lokalne funkcije

Možemo definirati funkcije unutar funkcija. Nazivaju se ugniježđenim funkcijama ili lokalnim funkcijama. Slično lokalnim varijablama, vidljivi su samo unutar funkcije u kojoj su definirani.

Napišimo sada metodu za izračunavanje snage pomoću ugniježđene funkcije:

def power (x: Int, y: Int): Int = {def powNested (i: Int, akumulator: Int): Int = {if (i <= 0) akumulator else powNested (i - 1, x * akumulator)} napuhano (y, 1)}

Dalje, provjerimo rezultat:

assertEquals (8, snaga (2, 3))

5.2. Funkcije višeg reda

Budući da su funkcije vrijednosti, možemo ih proslijediti kao parametre drugoj funkciji. Možemo imati i funkciju koja vraća drugu funkciju.

Funkcije koje djeluju na funkcije nazivamo funkcijama višeg reda. Omogućuju nam rad na apstraktnijoj razini. Pomoću njih možemo duplicirati kod pisanjem generaliziranih algoritama.

Napišimo sada funkciju višeg reda za izvođenje karte i smanjenje operacije u rasponu cijelih brojeva:

def mapReduce (r: (Int, Int) => Int, i: Int, m: Int => Int, a: Int, b: Int) = {def iter (a: Int, rezultat: Int): Int = { if (a> b) {result} else {iter (a + 1, r (m (a), result))}}} iter (a, i)}

Ovdje, r i m su parametri Funkcija tip. Prenošenjem različitih funkcija možemo riješiti niz problema, poput zbroja kvadrata ili kocki, i faktora.

Dalje, upotrijebimo ovu funkciju za pisanje druge funkcije zbrojevi kvadrata koji zbraja kvadrate cijelih brojeva:

@Test def whenCalledWithSumAndSquare_thenCorrectValue = {def square (x: Int) = x * x def sum (x: Int, y: Int) = x + y def sumSquares (a: Int, b: Int) = mapReduce (sum, 0, kvadrat, a, b) assertEquals (385, sumSquares (1, 10))}

Iznad, to možemo vidjeti funkcije višeg reda teže stvaranju mnogih malih funkcija za jednokratnu upotrebu. Imenovanje im možemo izbjeći pomoću anonimnih funkcija.

5.3. Anonimne funkcije

Anonimna funkcija izraz je koji ocjenjuje funkciju. Sličan je lambda izrazu u Javi.

Prepišimo prethodni primjer pomoću anonimnih funkcija:

@Test def whenCalledWithAnonymousFunctions_thenCorrectValue = {def sumSquares (a: Int, b: Int) = mapReduce ((x, y) => x + y, 0, x => x * x, a, b) assertEquals (385, sumSquares ( 1, 10))}

U ovom primjeru, mapReduce prima dvije anonimne funkcije: (x, y) => x + y i x => x * x.

Scala može odrediti vrste parametara iz konteksta. Stoga izostavljamo vrstu parametara u tim funkcijama.

To rezultira sažetijim kodom u odnosu na prethodni primjer.

5.4. Funkcije curryinga

Karirana funkcija uzima više popisa argumenata, kao što su def f (x: Int) (y: Int). Primjenjuje se prosljeđivanjem višestrukih popisa argumenata, kao u f (5) (6).

Ocjenjuje se kao pozivanje na lanac funkcija. Te posredne funkcije uzimaju jedan argument i vraćaju funkciju.

Također možemo djelomično odrediti popise argumenata, kao što su f (5).

Sada, shvatimo ovo na primjeru:

@Test def whenSumModCalledWith6And10_then10 = {// curried function def sum (f: Int => Int) (a: Int, b: Int): Int = if (a> b) 0 else f (a) + sum (f) (a + 1, b) // druga curried funkcija def mod (n: Int) (x: Int) = x% n // primjena curried funkcije assertEquals (1, mod (5) (6)) // djelomično primjena curried funkcije // prateća donja crta potrebna je da // tip funkcije učini eksplicitnim val sumMod5 = sum (mod (5)) _ assertEquals (10, sumMod5 (6, 10))}

Iznad, iznos i mod svaki uzima po dva argumenta.

Donosimo dva popisa argumenata poput mod (5) (6). To se procjenjuje kao dva poziva funkcije. Prvi, mod (5) procjenjuje se, što vraća funkciju. To se, pak, poziva argumentom 6. Rezultat je 1.

Moguće je djelomično primijeniti parametre kao u mod (5). Kao rezultat dobivamo funkciju.

Slično, u izrazu zbroj (mod (5)) _, prosljeđujemo samo prvi argument iznos funkcija. Stoga, sumMod5 je funkcija.

Podvlaka se koristi kao rezervirano mjesto za neprimijenjene argumente. Budući da prevoditelj ne može zaključiti da se očekuje vrsta funkcije, koristimo prateće donje crte kako bismo tip funkcije return postavili eksplicitnim.

5.5. Parametri po imenima

Funkcija može primijeniti parametre na dva različita načina - po vrijednosti i po imenu - procjenjuje argumente po vrijednosti samo jednom u trenutku poziva. Suprotno tome, procjenjuje poimenske argumente kad god su na njih upućeni. Ako se ne koristi argument by-name, on se ne procjenjuje.

Scala prema zadanim postavkama koristi parametre vrijednosti. Ako je ispred tipa parametra strelica (=>), on se prebacuje na parametar po imenu.

Sada, iskoristimo ga za implementaciju while petlje:

def whileLoop (condition: => Boolean) (body: => Unit): Unit = if (condition) {body whileLoop (condition) (body)}

Da bi gornja funkcija radila ispravno, oba parametra stanje i tijelo treba procijeniti svaki put kad se upućuju. Stoga ih definiramo kao parametre poimence.

6. Definicija razreda

Klasu definiramo s razred ključna riječ nakon koje slijedi naziv klase.

Nakon imena, možemo odrediti primarne parametre konstruktora. To automatski dodaje članove s istim imenom u razred.

U tijelu razreda definiramo članove - vrijednosti, varijable, metode itd. Oni su prema zadanim postavkama javni, osim ako ih nije izmijenio privatni ili zaštićen modifikatori pristupa.

Moramo koristiti nadjačati ključna riječ za poništavanje metode iz superklase.

Definirajmo zaposlenika klase:

klasa Zaposlenik (val ime: Niz, var plaća: Int, godišnji prirast: Int = 20) {def inkrementSalary (): Jedinica = {plata + = godišnji priraštaj} nadjačati def toString = s "Zaposlenik (ime = $ ime, plaća = $ plaća ) "}

Ovdje navodimo tri parametra konstruktora - Ime, plaća, i godišnji prirast.

Budući da izjavljujemo Ime i plaća s val i var ključne riječi, odgovarajući članovi su javni. S druge strane, ne koristimo val ili var ključna riječ za godišnji prirast parametar. Stoga je dopisni član privatni. Kako specificiramo zadanu vrijednost za ovaj parametar, možemo je izostaviti tijekom pozivanja konstruktora.

Uz polja definiramo i metodu prirastPlaća. Ova je metoda javna.

Dalje, napišite jedinstveni test za ovu klasu:

@Test def whenSalaryIncremented_thenCorrectSalary = {val zaposlenik = novi zaposlenik ("John Doe", 1000) worker.incrementSalary () assertEquals (1020, worker.salary)}

6.1. Sažetak klase

Koristimo ključnu riječ sažetak kako bi razred bio sažetak. Sličan je onome na Javi. Može imati sve članove koje može imati redovni razred.

Nadalje, može sadržavati apstraktne članove. To su članovi s samo izjavom i bez definicije, čija je definicija navedena u podrazredu.

Slično Java-i, ne možemo stvoriti instancu apstraktne klase.

A sad, ilustrirajmo apstraktni razred na primjeru.

Prvo, izradimo apstraktni razred IntSet za predstavljanje skupa cijelih brojeva:

apstraktna klasa IntSet {// dodavanje elementa u skup def uklj (x: Int): IntSet // pripada li element skupu def sadrži (x: Int): Boolean}

Dalje, kreirajmo konkretni podrazred EmptyIntSet za predstavljanje praznog skupa:

klasa EmptyIntSet proširuje IntSet {def sadrži (x: Int) = false def incl (x: Int) = new NonEmptyIntSet (x, this)}

Zatim, još jedan podrazred NonEmptyIntSet predstavljaju neprazne skupove:

klasa NonEmptyIntSet (val glava: Int, val rep: IntSet) proširuje IntSet {def sadrži (x: Int) = glava == x || (rep sadrži x) def incl (x: Int) = if (ovo sadrži x) {this} else {new NonEmptyIntSet (x, this)}}

Na kraju, napišimo jedinični test za NonEmptySet:

@Test def givenSetOf1To10_whenContains11Called_thenFalse = {// Postavljanje skupa koji sadrži cijele brojeve od 1 do 10. val set1To10 = Raspon (1, 10) .foldLeft (novi EmptyIntSet (): IntSet) {(x, y) => x uklj. Y} assertFalse (set1To10 sadrži 11)}

6.2. Osobine

Osobine odgovaraju Java sučeljima sa sljedećim razlikama:

  • sposoban proširiti se iz razreda
  • mogu pristupiti članovima superklase
  • mogu imati izjave inicijalizatora

Mi ih definiramo kao što definiramo klase, ali koristeći osobina ključna riječ. Osim toga, mogu imati iste članove kao i apstraktne klase, osim parametara konstruktora. Nadalje, oni su namijenjeni dodavanju u neku drugu klasu kao mixin.

A sad, ilustrirajmo osobine na primjeru.

Prvo, definirajmo osobinu UpperCasePrinter kako bi se osiguralo toString metoda vraća vrijednost velikim slovima:

osobina UpperCasePrinter {override def toString = super.toString toUpperCase}

Zatim, testirajmo ovu osobinu dodavanjem u Zaposlenik razred:

@Test def givenEfficieeWithTrait_whenToStringCalled_thenUpper = {val zaposlenik = novi zaposlenik ("John Doe", 10) s UpperCasePrinter assertEquals ("ZAPOSLENIK (IME = JOHN DOE, SALARY = 10)", worker.toString)}

Klase, objekti i osobine mogu naslijediti najviše jednu klasu, ali bilo koji broj osobina.

7. Definicija objekta

Objekti su primjerci klase. Kao što smo vidjeli u prethodnim primjerima, izrađujemo objekte iz klase pomoću novi ključna riječ.

Međutim, ako klasa može imati samo jednu instancu, moramo spriječiti stvaranje više instanci. U Javi koristimo Singleton obrazac da bismo to postigli.

Za takve slučajeve imamo sažetu sintaksu koja se naziva definicija objekta - slična definiciji klase s jednom razlikom. Umjesto da koristite razred koristimo ključnu riječ objekt ključna riječ. To definira klasu i lijeno stvara svoju jedinu instancu.

Definicije objekata koristimo za implementaciju korisnih metoda i pojedinačnih tonova.

Definirajmo a Utils objekt:

korisni objekti {def prosjek (x: Double, y: Double) = (x + y) / 2}

Ovdje definiramo klasu Utils a ujedno i stvaranje njegove jedine instance.

Mi se pozivamo na ovaj jedini primjer koristeći njegovo imeUtils. Ova se instanca kreira prvi put kad joj se pristupi.

Ne možemo stvoriti još jedan primjerak Utilsa pomoću novi ključna riječ.

Napišimo sada jedinstveni test za Utils objekt:

assertEquals (15.0, Utils.average (10, 20), 1e-5)

7.1. Predmet pratilaca i pratilac klase

Ako klasa i definicija objekta imaju isto ime, nazivamo ih kao popratna klasa, odnosno prateći objekt. Moramo definirati oboje u istoj datoteci. Popratni objekti mogu pristupiti privatnim članovima iz njihove pratiteljske klase i obrnuto.

Za razliku od Jave, nemamo statične članove. Umjesto toga, koristimo popratne objekte za implementaciju statičkih članova.

8. Usklađivanje uzoraka

Podudaranje uzorka podudara izraz s nizom alternativa. Svaki od njih započinje s ključnom riječi slučaj. Nakon toga slijedi obrazac, strelica za razdvajanje (=>) i niz izraza. Izraz se procjenjuje podudara li se obrazac.

Uzorke možemo graditi iz:

  • konstruktori klase slučaja
  • promjenjivi uzorak
  • uzorak zamjenskog znaka _
  • doslovce
  • stalni identifikatori

Klase slučajeva olakšavaju podudaranje uzoraka na objektima. Mi dodajemo slučaj ključna riječ dok definirate klasu kako bi ona postala klasa slučaja.

Dakle, podudaranje uzoraka mnogo je moćnije od naredbe switch u Javi. Iz tog razloga to je široko korištena jezična značajka.

Napišimo sada Fibonaccijevu metodu pomoću podudaranja uzoraka:

def fibonacci (n: Int): Int = n podudaranje 1 => 1 slučaj x ako je x> 1 => fibonacci (x-1) + fibonacci (x-2) 

Dalje, napišimo jedinični test za ovu metodu:

assertEquals (13, fibonacci (6))

9. Zaključak

U ovom uputstvu predstavili smo jezik Scala i neke od njegovih ključnih značajki. Kao što smo vidjeli, pruža izvrsnu podršku za imperativno, funkcionalno i objektno orijentirano programiranje.

Kao i obično, puni izvorni kod možete pronaći na GitHubu.


$config[zx-auto] not found$config[zx-overlay] not found