Scala Language
samlingar
Sök…
Sortera en lista
Om vi antar följande lista kan vi sortera olika sätt.
val names = List("Kathryn", "Allie", "Beth", "Serin", "Alana")
Standardbeteendet för sorted()
är att använda math.Ordering
, vilket för strängar resulterar i en lexografisk sortering:
names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)
sortWith
låter dig tillhandahålla din egen beställning genom att använda en jämförelsefunktion:
names.sortWith(_.length < _.length)
// results in: List(Beth, Allie, Serin, Alana, Kathryn)
sortBy
tillåter dig att tillhandahålla en transformationsfunktion:
//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)
Du kan alltid vända en lista eller en sorterad lista med "omvänd:
names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)
Listor kan också sorteras med Java-metoden java.util.Arrays.sort
och dess Scala-omslag scala.util.Sorting.quickSort
java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)
Dessa metoder kan förbättra prestandan när du sorterar större samlingar om samlingskonvertering och unboxing / boxning kan undvikas. För en mer detaljerad diskussion om prestationsskillnader, läs om Scala Collection sorterade, sortWith och sortBy Performance .
Skapa en lista som innehåller n kopior av x
För att skapa en samling n
kopior av något objekt x
, använd fyllningsmetoden . Det här exemplet skapar en List
, men det kan fungera med andra samlingar för vilka fill
är vettigt:
// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)
Lista och vektorfusk
Det är nu en bra praxis att använda
Vector
istället förList
eftersom implementeringarna har bättre prestanda Prestandaegenskaper finns här .Vector
kan användas varList
används.
Lista skapande
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
Ta 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
Förbered element
0 :: List(1, 2, 3) // List(0, 1, 2, 3)
Lägg till element
List(1, 2, 3) :+ 4 // List(1, 2, 3, 4), complexity is O(n)
Gå med (sammanlänkade) listor
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)
Vanliga operationer
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"
Kartsamling Cheatsheet
Observera att detta handlar om att skapa en samling av typen
Map
, som skiljer sig frånmap
metoden.
Skapa kartan
Map[String, Int]()
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()
En karta kan betraktas som en samling tuples
för de flesta operationer, där det första elementet är nyckeln och det andra är värdet.
val l = List(("a", 1), ("b", 2), ("c", 3))
val m = l.toMap // Map(a -> 1, b -> 2, c -> 3)
Skaffa 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)
Lägg till element
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)
Vanliga operationer
I operationer där en iteration över en karta inträffar ( map
, find
, för forEach
osv.) forEach
elementen i samlingen tuples
. Funktionsparametern kan antingen använda tuple-accessorer ( _1
, _2
) eller en partiell funktion med ett fallblock:
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
Karta och filtrera över en samling
Karta
'Mapping' över en samling använder map
funktionen att transformera varje element i samlingen på ett liknande sätt. Den allmänna syntaxen är:
val someFunction: (A) => (B) = ???
collection.map(someFunction)
Du kan tillhandahålla en anonym funktion:
collection.map((x: T) => /*Do something with x*/)
Multiplicera heltal med två
// 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)
Filtrera
filter
används när du vill utesluta eller "filtrera bort" vissa element i en samling. Liksom med map
den allmänna syntaxen en funktion, men den funktionen måste returnera en Boolean
:
val someFunction: (a) => Boolean = ???
collection.filter(someFunction)
Du kan tillhandahålla en anonym funktion direkt:
collection.filter((x: T) => /*Do something that returns a Boolean*/)
Kontrollerar parnumren
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)
Fler exempel på karta och filter
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 )
Introduktion till Scala-samlingar
Scala Collections-ramverket är enligt författarna utformat för att vara lätt att använda, kortfattat, säkert, snabbt och universellt.
Ramverket består av Scala- drag som är utformade som byggstenar för att skapa samlingar. För mer information om dessa byggstenar, läs den officiella översikten över Scala-samlingar .
Dessa inbyggda samlingar är separerade i de immutable och mutable paketen. Som standard används de immutable versionerna. Att konstruera en List()
(utan att importera någonting) kommer att skapa en oföränderlig lista.
En av de mest kraftfulla funktionerna i ramverket är det konsekventa och lättanvända gränssnittet över likasinnade samlingar. Exempelvis är summan av alla element i en samling densamma för listor, uppsättningar, vektorer, sekvenser och matriser:
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
Dessa likasinnade typer ärver från Traversable
egenskapen.
Det är nu en bra praxis att använda
Vector
istället förList
eftersom implementeringarna har bättre prestanda Prestandaegenskaper finns här .Vector
kan användas varList
används.
Traversabla typer
Samlingsklasser som har Traversable
drag implementerar foreach
och ärver många metoder för att utföra vanliga operationer till samlingar, som alla fungerar identiskt. De vanligaste operationerna listas här:
- Karta -
map
,flatMap
ochcollect
producerar nya samlingar genom att tillämpa en funktion på varje element i den ursprungliga samlingen.
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")
- Konverteringar -
toList
,toArray
och många andra konverteringsoperationer förändrar den aktuella samlingen till en mer specifik typ av samling. Det här är vanligtvis metoder förberedda med 'till' och den mer specifika typen (dvs. 'toList' konverteras till enList
).
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
- Storleksinfo -
isEmpty
,nonEmpty
,size
ochhasDefiniteSize
är alla metadata om uppsättningen. Detta tillåter villkorade operationer på samlingen, eller för kod för att avgöra storleken på samlingen, inklusive om det är oändligt eller diskret.
List().isEmpty // true
List(1).nonEmpty // true
- Elementhämtning -
head
,last
,find
och derasOption
används för att hämta det första eller det sista elementet, eller hitta ett specifikt element i samlingen.
val list = List(1, 2, 3)
list.head // = 1
list.last // = 3
- Återvinning av undersamling -
filter
,tail
,slice
,drop
och andra funktioner gör det möjligt att välja delar av samlingen att arbeta vidare.
List(-2, -1, 0, 1, 2).filter(num => num > 0) // = List(1, 2)
- Underdelningsoperationer -
partition
,splitAt
,span
ochgroupBy
delar upp den nuvarande samlingen i olika delar.
// split numbers into < 0 and >= 0
List(-2, -1, 0, 1, 2).partition(num => num < 0) // = (List(-2, -1), List(0, 1, 2))
- Elementtester -
exists
, förforall
ochcount
är operationer som används för att kontrollera denna samling för att se om den uppfyller ett predikat.
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
ochreduceRight
används för att tillämpa binära funktioner på successiva element i samlingen. Gå hit för exempel på exempel och gå här för att minska exempel .
Vika ihop
fold
iterates över en samling med ett initialt ackumuleringsvärde och tillämpar en funktion som använder varje element för att uppdatera ackumulatorn framgångsrikt:
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
I exemplet ovan levererades en anonym funktion för att fold()
. Du kan också använda en namngiven funktion som tar två argument. Med detta i mitt kan exemplet ovan skrivas om så:
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
Ändring av det initiala värdet påverkar resultatet:
initialValue = 2;
sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17
fold
har två varianter - foldLeft
och foldRight
.
foldLeft()
iterates från vänster till höger (från det första elementet i samlingen till det sista i den ordningen). foldRight()
iterates från höger till vänster (från det sista elementet till det första elementet). fold()
iterates från vänster till höger som foldLeft()
. Faktum är att fold()
kallar faktiskt foldLeft()
internt.
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
fold()
, foldLeft()
och foldRight()
kommer att returnera ett värde som har samma typ med det initiala värdet det tar. Till skillnad från foldLeft()
och foldRight()
kan emellertid det initiala värdet som ges till fold()
endast vara av samma typ eller en supertyp av samlingen.
I det här exemplet är ordningen inte relevant, så du kan ändra fold()
till foldLeft()
eller foldRight()
så blir resultatet detsamma. Om du använder en funktion som är känslig för ordning kommer resultatet att förändras.
Om du är osäker, föredrar foldLeft()
framför foldRight()
. foldRight()
är mindre performant.
För varje
foreach
är ovanligt bland samlingarna iteratorer i och med att det inte ger ett resultat. Istället tillämpar det en funktion på varje element som endast har biverkningar. Till exempel:
scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> x.foreach { println }
1
2
3
Funktionen som tillhandahålls för foreach
kan ha valfri foreach
, men resultatet kommer att kasseras . Vanligtvis används foreach
när biverkningar är önskvärda. Om du vill omvandla data kan du överväga att använda map
, filter
, en for comprehension
eller ett annat alternativ.
Exempel på kassering av resultat
def myFunc(a: Int) : Int = a * 2
List(1,2,3).foreach(myFunc) // Returns nothing
Minska
reduceLeft()
reduce()
, reduceLeft()
och reduceRight
liknar veck. Funktionen som ges för att minska tar två värden och ger en tredje. När du arbetar på en lista är de två första värdena de två första värdena i listan. Resultatet av funktionen och nästa värde i listan appliceras sedan igen på funktionen, vilket ger ett nytt resultat. Detta nya resultat tillämpas med nästa värde på listan och så vidare tills det inte finns fler element. Det slutliga resultatet returneras.
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
Det finns vissa skillnader i hur reduceringsfunktionerna fungerar jämfört med vikfunktionerna. Dom är:
- Minskningsfunktionerna har inget initialt ackumuleringsvärde.
- Minska funktioner kan inte kallas på tomma listor.
- Minska funktioner kan bara returnera listans typ eller supertyp.