Scala Language
collezioni
Ricerca…
Ordina una lista
Supponendo che il seguente elenco possiamo ordinare una varietà di modi.
val names = List("Kathryn", "Allie", "Beth", "Serin", "Alana")
Il comportamento predefinito di sorted()
consiste nell'utilizzare math.Ordering
, che per le stringhe restituisce un ordinamento lessografico :
names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)
sortWith
consente di fornire il proprio ordine utilizzando una funzione di confronto:
names.sortWith(_.length < _.length)
// results in: List(Beth, Allie, Serin, Alana, Kathryn)
sortBy
ti consente di fornire una funzione di trasformazione:
//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)
Puoi sempre invertire una lista, o una lista ordinata, usando `reverse:
names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)
Gli elenchi possono anche essere ordinati utilizzando il metodo Java java.util.Arrays.sort
e il suo wrapper Scala scala.util.Sorting.quickSort
java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)
Questi metodi possono migliorare le prestazioni quando si ordinano raccolte più grandi, se è possibile evitare le conversioni di raccolta e di disimballaggio / boxing. Per una discussione più dettagliata sulle differenze di prestazioni, leggi su Scala Collection ordinata, sortWith e sortBy Performance .
Crea una lista contenente n copie di x
Per creare una raccolta di n
copie di qualche oggetto x
, utilizzare il metodo di riempimento . Questo esempio crea un List
, ma può funzionare con altre raccolte per le quali il fill
ha senso:
// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)
Lista e trucchi di vettore
Ora è preferibile utilizzare
Vector
anzichéList
perché le implementazioni hanno prestazioni migliori. Le caratteristiche di prestazione possono essere trovate qui .Vector
può essere utilizzato ovunque sia utilizzatoList
.
Creazione di liste
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
Prendi l'elemento
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
Prepend gli elementi
0 :: List(1, 2, 3) // List(0, 1, 2, 3)
Aggiungi elementi
List(1, 2, 3) :+ 4 // List(1, 2, 3, 4), complexity is O(n)
Iscriviti (Concatena) Elenchi
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)
Operazioni comuni
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"
Tagliere di raccolta mappe
Nota che questo riguarda la creazione di una raccolta di tipo
Map
, che è distinta dal metodo dellamap
.
Creazione della mappa
Map[String, Int]()
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()
Una mappa può essere considerata una raccolta di tuples
per la maggior parte delle operazioni, in cui il primo elemento è la chiave e il secondo è il valore.
val l = List(("a", 1), ("b", 2), ("c", 3))
val m = l.toMap // Map(a -> 1, b -> 2, c -> 3)
Ottieni elemento
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)
Aggiungi elemento (i)
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)
Operazioni comuni
Nelle operazioni in cui si verifica un'iterazione su una mappa ( map
, find
, forEach
, ecc.), Gli elementi della raccolta sono tuples
. Il parametro della funzione può utilizzare gli accessor tuple ( _1
, _2
) o una funzione parziale con un blocco di casi:
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
Mappa e filtra su una collezione
Carta geografica
"Mappatura" su una raccolta utilizza la funzione map
per trasformare ogni elemento di quella raccolta in un modo simile. La sintassi generale è:
val someFunction: (A) => (B) = ???
collection.map(someFunction)
Puoi fornire una funzione anonima:
collection.map((x: T) => /*Do something with x*/)
Moltiplicare i numeri interi per due
// 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)
Filtro
filter
viene utilizzato quando si desidera escludere o "filtrare" determinati elementi di una raccolta. Come con la map
, la sintassi generale assume una funzione, ma quella funzione deve restituire un Boolean
:
val someFunction: (a) => Boolean = ???
collection.filter(someFunction)
Puoi fornire direttamente una funzione anonima:
collection.filter((x: T) => /*Do something that returns a Boolean*/)
Controllo dei numeri di coppia
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)
Altri esempi di mappe e filtri
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 )
Introduzione alle Collezioni Scala
Il framework delle collezioni Scala, secondo i suoi autori , è progettato per essere facile da usare, conciso, sicuro, veloce e universale.
Il framework è costituito da tratti Scala che sono progettati per essere blocchi per la creazione di collezioni. Per maggiori informazioni su questi blocchi, leggi la panoramica ufficiale delle collezioni Scala .
Queste raccolte incorporate sono separate nei pacchetti immutabili e mutabili. Per impostazione predefinita, vengono utilizzate le versioni immutabili. Costruire un List()
(senza importare nulla) costruirà una lista immutabile .
Una delle funzionalità più potenti del framework è l'interfaccia coerente e di facile utilizzo tra le raccolte affini. Ad esempio, sommando tutti gli elementi in una raccolta è uguale per Elenchi, Set, Vettori, Seq e Matrici:
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
Questi tipi affini ereditano dal tratto Traversable
.
Ora è preferibile utilizzare
Vector
anzichéList
perché le implementazioni hanno prestazioni migliori. Le caratteristiche di prestazione possono essere trovate qui .Vector
può essere utilizzato ovunque sia utilizzatoList
.
Tipi percorribili
Classi di raccolta che hanno l'attrezzo Traversable
trait foreach
ed ereditano molti metodi per eseguire operazioni comuni alle collezioni, che funzionano tutte in modo identico. Le operazioni più comuni sono elencate qui:
- Mappa :
map
,flatMap
ecollect
nuove collezioni applicando una funzione a ciascun elemento della raccolta originale.
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")
- Le conversioni -
toList
,toArray
e molte altre operazioni di conversione cambiano la raccolta corrente in un tipo più specifico di raccolta. Questi sono in genere metodi preceduti da 'to' e il tipo più specifico (cioè 'tolist' converte in unaList
).
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
- Le informazioni sulla dimensione -
isEmpty
,nonEmpty
,size
ehasDefiniteSize
sono tutti metadati relativi al set. Ciò consente alle operazioni condizionali sulla raccolta o al codice di determinare la dimensione della raccolta, incluso se è infinita o discreta.
List().isEmpty // true
List(1).nonEmpty // true
- Recupero degli elementi :
head
,last
,find
e le loro varianti diOption
vengono utilizzate per recuperare il primo o l'ultimo elemento o trovare un elemento specifico nella raccolta.
val list = List(1, 2, 3)
list.head // = 1
list.last // = 3
- Le operazioni di recupero della sub-raccolta -
filter
,tail
,slice
,drop
e altre operazioni consentono di scegliere parti della raccolta per operare ulteriormente.
List(-2, -1, 0, 1, 2).filter(num => num > 0) // = List(1, 2)
- Operazioni di suddivisione :
partition
,splitAt
,span
egroupBy
dividono la raccolta corrente in parti diverse.
// split numbers into < 0 and >= 0
List(-2, -1, 0, 1, 2).partition(num => num < 0) // = (List(-2, -1), List(0, 1, 2))
- Test Element -
exists
,forall
ecount
vengono usati per controllare le operazioni di questa raccolta per vedere se soddisfa un predicato.
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
ereduceRight
vengono utilizzati per applicare le funzioni binarie agli elementi successivi nella raccolta. Vai qui per gli esempi di piega e vai qui per ridurre gli esempi .
piega
Il metodo fold
esegue iterazioni su una raccolta, utilizzando un valore dell'accumulatore iniziale e applicando una funzione che utilizza ciascun elemento per aggiornare correttamente l'accumulatore:
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
Nell'esempio precedente, è stata fornita una funzione anonima a fold()
. Puoi anche usare una funzione con nome che accetta due argomenti. Sopportando questo nel mio, l'esempio sopra può essere riscritto così:
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
La modifica del valore iniziale influirà sul risultato:
initialValue = 2;
sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17
Il metodo fold
ha due varianti: foldLeft
e foldRight
.
foldLeft()
itera da sinistra a destra (dal primo elemento della raccolta fino all'ultimo in quell'ordine). foldRight()
da destra a sinistra (dall'ultimo elemento al primo elemento). fold()
scorre da sinistra a destra come foldLeft()
. Infatti fold()
chiama effettivamente foldLeft()
internamente.
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
fold()
, foldLeft()
e foldRight()
restituiranno un valore che ha lo stesso tipo con il valore iniziale che prende. Tuttavia, a differenza di foldLeft()
e foldRight()
, il valore iniziale assegnato a fold()
può essere solo dello stesso tipo o di un supertipo del tipo della raccolta.
In questo esempio l'ordine non è rilevante, quindi puoi cambiare fold()
in foldLeft()
o foldRight()
e il risultato rimarrà lo stesso. L'utilizzo di una funzione sensibile all'ordine altererà i risultati.
In caso di dubbio, preferisci foldLeft()
su foldRight()
. foldRight()
è meno performante.
Per ciascuno
foreach
è insolito tra gli iteratori delle collezioni in quanto non restituisce un risultato. Invece applica una funzione a ciascun elemento che ha solo effetti collaterali. Per esempio:
scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> x.foreach { println }
1
2
3
La funzione fornita a foreach
può avere qualsiasi tipo di ritorno, ma il risultato verrà scartato . In genere foreach
viene utilizzato quando sono desiderati effetti collaterali. Se si desidera trasformare i dati, prendere in considerazione l'utilizzo di map
, filter
, a for comprehension
o un'altra opzione.
Esempio di risultati di scarto
def myFunc(a: Int) : Int = a * 2
List(1,2,3).foreach(myFunc) // Returns nothing
Ridurre
I metodi reduceRight
reduce()
, reduceLeft()
e reduceRight
sono simili alle pieghe. La funzione passata per ridurre prende due valori e ne produce un terzo. Quando si opera in un elenco, i primi due valori sono i primi due valori nell'elenco. Il risultato della funzione e il successivo valore nell'elenco vengono quindi riapplicati alla funzione, producendo un nuovo risultato. Questo nuovo risultato viene applicato con il successivo valore dell'elenco e così via fino a quando non ci sono più elementi. Il risultato finale viene restituito.
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
Ci sono alcune differenze nel modo in cui le funzioni di riduzione funzionano rispetto alle funzioni di piega. Loro sono:
- Le funzioni di riduzione non hanno valore dell'accumulatore iniziale.
- Le funzioni di riduzione non possono essere chiamate su liste vuote.
- Le funzioni di riduzione possono solo restituire il tipo o il supertipo dell'elenco.