Scala Language
collecties
Zoeken…
Sorteer een lijst
Als we de volgende lijst veronderstellen, kunnen we op verschillende manieren sorteren.
val names = List("Kathryn", "Allie", "Beth", "Serin", "Alana")
Het standaardgedrag van math.Ordering
sorted()
is het gebruik van math.Ordering
, wat voor strings resulteert in een lexografische sortering:
names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)
sortWith
kunt u uw eigen bestelling sortWith
met een vergelijkingsfunctie:
names.sortWith(_.length < _.length)
// results in: List(Beth, Allie, Serin, Alana, Kathryn)
sortBy
kunt u een transformatiefunctie bieden:
//A set of vowels to use
val vowels = Set('a', 'e', 'i', 'o', 'u')
//A function that counts the vowels in a name
def countVowels(name: String) = name.count(l => vowels.contains(l.toLower))
//Sorts by the number of vowels
names.sortBy(countVowels)
//result is: List(Kathryn, Beth, Serin, Allie, Alana)
U kunt een lijst of een gesorteerde lijst altijd omdraaien met `reverse:
names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)
Lijsten kunnen ook worden gesorteerd met behulp van de Java-methode java.util.Arrays.sort
en de Scala wrapper scala.util.Sorting.quickSort
java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)
Deze methoden kunnen de prestaties verbeteren bij het sorteren van grotere collecties als de collectieconversies en unboxing / boxing kunnen worden vermeden. Lees Scala Collection sort, sortWith en sortBy Performance voor meer informatie over de prestatieverschillen.
Maak een lijst met n exemplaren van x
Een collectie maken n
kopieën van een voorwerp x
Gebruik de vulling methode. In dit voorbeeld wordt een List
, maar dit kan werken met andere collecties waarvoor fill
zinvol is:
// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)
Lijst en Vector Cheatsheet
Het is nu een best practice om
Vector
plaats vanList
omdat de implementaties betere prestaties hebben. Prestatiekenmerken zijn hier te vinden .Vector
kan overal worden gebruikt waarList
wordt gebruikt.
Lijst maken
List[Int]() // Declares an empty list of type Int
List.empty[Int] // Uses `empty` method to declare empty list of type Int
Nil // A list of type Nothing that explicitly has nothing in it
List(1, 2, 3) // Declare a list with some elements
1 :: 2 :: 3 :: Nil // Chaining element prepending to an empty list, in a LISP-style
Neem element
List(1, 2, 3).headOption // Some(1)
List(1, 2, 3).head // 1
List(1, 2, 3).lastOption // Some(3)
List(1, 2, 3).last // 3, complexity is O(n)
List(1, 2, 3)(1) // 2, complexity is O(n)
List(1, 2, 3)(3) // java.lang.IndexOutOfBoundsException: 4
Elementen voorafgaan
0 :: List(1, 2, 3) // List(0, 1, 2, 3)
Voeg elementen toe
List(1, 2, 3) :+ 4 // List(1, 2, 3, 4), complexity is O(n)
Deelnemen aan (samengevoegde) lijsten
List(1, 2) ::: List(3, 4) // List(1, 2, 3, 4)
List.concat(List(1,2), List(3, 4)) // List(1, 2, 3, 4)
List(1, 2) ++ List(3, 4) // List(1, 2, 3, 4)
Gemeenschappelijke operaties
List(1, 2, 3).find(_ == 3) // Some(3)
List(1, 2, 3).map(_ * 2) // List(2, 4, 6)
List(1, 2, 3).filter(_ % 2 == 1) // List(1, 3)
List(1, 2, 3).fold(0)((acc, i) => acc + i * i) // 1 * 1 + 2 * 2 + 3 * 3 = 14
List(1, 2, 3).foldLeft("Foo")(_ + _.toString) // "Foo123"
List(1, 2, 3).foldRight("Foo")(_ + _.toString) // "123Foo"
Cheatsheet voor kaartenverzameling
Merk op dat deze deals met de oprichting van een verzameling van het type
Map
, die zich onderscheidt van demap
methode.
Kaart maken
Map[String, Int]()
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()
Een kaart kan voor de meeste bewerkingen worden beschouwd als een verzameling tuples
, waarbij het eerste element de sleutel is en het tweede de waarde.
val l = List(("a", 1), ("b", 2), ("c", 3))
val m = l.toMap // Map(a -> 1, b -> 2, c -> 3)
Krijg element
val m = Map("a" -> 1, "b" -> 2, "c" -> 3)
m.get("a") // Some(1)
m.get("d") // None
m("a") // 1
m("d") // java.util.NoSuchElementException: key not found: d
m.keys // Set(a, b, c)
m.values // MapLike(1, 2, 3)
Element (en) toevoegen
Map("a" -> 1, "b" -> 2) + ("c" -> 3) // Map(a -> 1, b -> 2, c -> 3)
Map("a" -> 1, "b" -> 2) + ("a" -> 3) // Map(a -> 3, b -> 2)
Map("a" -> 1, "b" -> 2) ++ Map("b" -> 3, "c" -> 4) // Map(a -> 1, b -> 3, c -> 4)
Gemeenschappelijke operaties
Bij bewerkingen waarbij een iteratie over een kaart plaatsvindt ( map
, find
, forEach
, enz.), forEach
de elementen van de verzameling tuples
. De functieparameter kan de tuple-accessors ( _1
, _2
) of een gedeeltelijke functie met een casusblok gebruiken:
m.find(_._1 == "a") // Some((a,1))
m.map {
case (key, value) => (value, key)
} // Map(1 -> a, 2 -> b, 3 -> c)
m.filter(_._2 == 2) // Map(b -> 2)
m.foldLeft(0){
case (acc, (key, value: Int)) => acc + value
} // 6
Kaart en filter over een verzameling
Kaart
'Mapping' over een collectie maakt gebruik van de map
functie om elk element van die collectie te transformeren in een vergelijkbare manier. De algemene syntaxis is:
val someFunction: (A) => (B) = ???
collection.map(someFunction)
U kunt een anonieme functie bieden:
collection.map((x: T) => /*Do something with x*/)
Vermenigvuldigen met gehele getallen met twee
// Initialize
val list = List(1,2,3)
// list: List[Int] = List(1, 2, 3)
// Apply map
list.map((item: Int) => item*2)
// res0: List[Int] = List(2, 4, 6)
// Or in a more concise way
list.map(_*2)
// res1: List[Int] = List(2, 4, 6)
Filter
filter
wordt gebruikt wanneer u bepaalde elementen van een verzameling wilt uitsluiten of 'filteren'. Net als bij map
heeft de algemene syntaxis een functie, maar die functie moet een Boolean
retourneren:
val someFunction: (a) => Boolean = ???
collection.filter(someFunction)
U kunt rechtstreeks een anonieme functie bieden:
collection.filter((x: T) => /*Do something that returns a Boolean*/)
Paarnummers controleren
val list = 1 to 10 toList
// list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Filter out all elements that aren't evenly divisible by 2
list.filter((item: Int) => item % 2==0)
// res0: List[Int] = List(2, 4, 6, 8, 10)
Meer kaart- en filtervoorbeelden
case class Person(firstName: String,
lastName: String,
title: String)
// Make a sequence of people
val people = Seq(
Person("Millie", "Fletcher", "Mrs"),
Person("Jim", "White", "Mr"),
Person("Jenny", "Ball", "Miss") )
// Make labels using map
val labels = people.map( person =>
s"${person.title}. ${person.lastName}"
)
// Filter the elements beginning with J
val beginningWithJ = people.filter(_.firstName.startsWith("J"))
// Extract first names and concatenate to a string
val firstNames = people.map(_.firstName).reduce( (a, b) => a + "," + b )
Inleiding tot Scala-collecties
Het Scala Collections-raamwerk is volgens de auteurs ontworpen om gemakkelijk te gebruiken, beknopt, veilig, snel en universeel te zijn.
Het raamwerk bestaat uit Scala- eigenschappen die zijn ontworpen als bouwstenen voor het maken van collecties. Lees het officiële Scala-collectiesoverzicht voor meer informatie over deze bouwstenen.
Deze ingebouwde collecties zijn gescheiden in de onveranderlijke en veranderlijke pakketten. Standaard worden de onveranderlijke versies gebruikt. Een List()
samenstellen List()
(zonder iets te importeren) zal een onveranderlijke lijst samenstellen.
Een van de krachtigste functies van het framework is de consistente en gemakkelijk te gebruiken interface in gelijkgestemde collecties. Het optellen van alle elementen in een verzameling is bijvoorbeeld hetzelfde voor Lijsten, Sets, Vectoren, Seqs en Arrays:
val numList = List[Int](1, 2, 3, 4, 5)
numList.reduce((n1, n2) => n1 + n2) // 15
val numSet = Set[Int](1, 2, 3, 4, 5)
numSet.reduce((n1, n2) => n1 + n2) // 15
val numArray = Array[Int](1, 2, 3, 4, 5)
numArray.reduce((n1, n2) => n1 + n2) // 15
Deze gelijkgestemde types erven van de Traversable
eigenschap.
Het is nu een best practice om
Vector
plaats vanList
omdat de implementaties betere prestaties hebben. Prestatiekenmerken zijn hier te vinden .Vector
kan overal worden gebruikt waarList
wordt gebruikt.
Traversable types
Collection klassen die het hebben Traversable
eigenschap implementeren foreach
en erven vele methoden voor het uitvoeren van algemene handelingen voor collecties, die alle werken op dezelfde wijze. De meest voorkomende bewerkingen worden hier vermeld:
- Map -
map
,flatMap
encollect
produceren nieuwe collecties door een functie toe te passen op elk element in de originele collectie.
List(1, 2, 3).map(num => num * 2) // double every number = List(2, 4, 6)
// split list of letters into individual strings and put them into the same list
List("a b c", "d e").flatMap(letters => letters.split(" ")) // = List("a", "b", "c", "d", "e")
- Conversies -
toList
,toArray
en vele andere conversiebewerkingen veranderen de huidige collectie in een meer specifiek soort collectie. Dit zijn meestal methoden voorafgegaan door 'naar' en het meer specifieke type (dat wil zeggen 'naarLijst' converteert naar eenList
).
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
- Grootte-informatie - is
isEmpty
, nietnonEmpty
,size
enhasDefiniteSize
zijn allemaal metagegevens over de set. Dit maakt voorwaardelijke bewerkingen op de verzameling mogelijk, of voor code om de grootte van de verzameling te bepalen, inclusief of deze oneindig of discreet is.
List().isEmpty // true
List(1).nonEmpty // true
- Element retrieval -
head
,last
,find
, en hunOption
varianten worden gebruikt om de eerste of laatste element op te halen, of zoek een specifiek element in de collectie.
val list = List(1, 2, 3)
list.head // = 1
list.last // = 3
- Ophaalbewerkingen van subverzamelingen -
filter
,tail
,slice
,drop
en andere bewerkingen laten toe om delen van de verzameling te kiezen om verder te werken.
List(-2, -1, 0, 1, 2).filter(num => num > 0) // = List(1, 2)
- Onderverdelingsbewerkingen -
partition
,splitAt
,span
engroupBy
splitst de huidige collectie in verschillende delen.
// split numbers into < 0 and >= 0
List(-2, -1, 0, 1, 2).partition(num => num < 0) // = (List(-2, -1), List(0, 1, 2))
- Element testen -
exists
,forall
encount
worden operaties gebruikt om deze collectie te controleren of het voldoet aan een predikaat te zien.
List(1, 2, 3, 4).forall(num => num > 0) // = true, all numbers are positive
List(-3, -2, -1, 1).forall(num => num < 0) // = false, not all numbers are negative
- Folds -
foldLeft
(/:
foldRight
,foldRight
(:\
),reduceLeft
enreduceRight
worden gebruikt om binaire functies toe te passen op opeenvolgende elementen in de verzameling. Ga hier voor vouwvoorbeelden en ga hier voor voorbeelden verkleinen .
Vouwen
De fold
Werkwijze doorloopt een verzameling met behulp van een initiële accumulatorwaarde en toepassen van een functie die ieder element gebruikt om de accumulator succesvol werken:
val nums = List(1,2,3,4,5)
var initialValue:Int = 0;
var sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 15 because 0+1+2+3+4+5 = 15
In het bovenstaande voorbeeld werd een anonieme functie geleverd om te fold()
. U kunt ook een benoemde functie gebruiken waarvoor twee argumenten nodig zijn. Met dit in mijn, kan het bovenstaande voorbeeld als volgt worden herschreven:
def sum(x: Int, y: Int) = x+ y
val nums = List(1, 2, 3, 4, 5)
var initialValue: Int = 0
val sum = nums.fold(initialValue)(sum)
println(sum) // prints 15 because 0 + 1 + 2 + 3 + 4 + 5 = 15
Het wijzigen van de beginwaarde heeft invloed op het resultaat:
initialValue = 2;
sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17
De fold
methode heeft twee varianten - foldLeft
en foldRight
.
foldLeft()
van links naar rechts foldLeft()
van het eerste element van de verzameling tot het laatste in die volgorde). foldRight()
itereert van rechts naar links (van het laatste element naar het eerste element). fold()
itereert van links naar rechts zoals foldLeft()
. In feite roept fold()
feite intern foldLeft()
.
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
fold()
, foldLeft()
en foldRight()
retourneren een waarde van hetzelfde type met de initiële waarde die nodig is. Anders dan foldLeft()
en foldRight()
, kan de initiële waarde die aan fold()
echter alleen van hetzelfde type of een supertype van het type van de verzameling zijn.
In dit voorbeeld is de volgorde niet relevant, dus u kunt fold()
in foldLeft()
of foldRight()
en het resultaat blijft hetzelfde. Het gebruik van een functie die gevoelig is voor bestelling zal de resultaten veranderen.
Geef bij twijfel de voorkeur aan foldLeft()
boven foldRight()
. foldRight()
is minder performant.
foreach
foreach
is ongebruikelijk onder de iterators van collecties omdat het geen resultaat oplevert. In plaats daarvan past het een functie toe op elk element dat alleen bijwerkingen heeft. Bijvoorbeeld:
scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> x.foreach { println }
1
2
3
De functie die aan foreach
geleverd, kan elk foreach
hebben, maar het resultaat wordt genegeerd . Meestal wordt foreach
gebruikt wanneer bijwerkingen wenselijk zijn. Als u gegevens wilt transformeren, kunt u overwegen om map
, filter
, een for comprehension
of een andere optie te gebruiken.
Voorbeeld van weggooien van resultaten
def myFunc(a: Int) : Int = a * 2
List(1,2,3).foreach(myFunc) // Returns nothing
Verminderen
De methoden reduce()
, reduceLeft()
en reduceRight
zijn vergelijkbaar met vouwen. De functie die is doorgegeven om te verkleinen, heeft twee waarden en levert een derde op. Wanneer u in een lijst werkt, zijn de eerste twee waarden de eerste twee waarden in de lijst. Het resultaat van de functie en de volgende waarde in de lijst worden vervolgens opnieuw toegepast op de functie, wat een nieuw resultaat oplevert. Dit nieuwe resultaat wordt toegepast met de volgende waarde van de lijst enzovoort totdat er geen elementen meer zijn. Het eindresultaat is terug.
val nums = List(1,2,3,4,5)
sum = nums.reduce({ (a, b) => a + b })
println(sum) //prints 15
val names = List("John","Koby", "Josh", "Matilda", "Zac", "Mary Poppins")
def findLongest(nameA:String, nameB:String):String = {
if (nameA.length > nameB.length) nameA else nameB
}
def findLastAlphabetically(nameA:String, nameB:String):String = {
if (nameA > nameB) nameA else nameB
}
val longestName:String = names.reduce(findLongest(_,_))
println(longestName) //prints Mary Poppins
//You can also omit the arguments if you want
val lastAlphabetically:String = names.reduce(findLastAlphabetically)
println(lastAlphabetically) //prints Zac
Er zijn enkele verschillen in hoe de verkleiningsfuncties werken in vergelijking met de vouwfuncties. Zij zijn:
- De verkleiningsfuncties hebben geen initiële accumulatiewaarde.
- Beperkingsfuncties kunnen niet worden opgeroepen in lege lijsten.
- Met functies verkleinen kunt u alleen het type of het supertype van de lijst retourneren.