Scala Language
Colecciones
Buscar..
Ordenar una lista
Suponiendo que en la siguiente lista podemos clasificar una variedad de formas.
val names = List("Kathryn", "Allie", "Beth", "Serin", "Alana")
El comportamiento predeterminado de sorted()
es usar math.Ordering
, que para cadenas da como resultado una ordenación lexográfica :
names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)
sortWith
permite proporcionar su propio pedido utilizando una función de comparación:
names.sortWith(_.length < _.length)
// results in: List(Beth, Allie, Serin, Alana, Kathryn)
sortBy
permite proporcionar una función de transformación:
//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)
Siempre puede revertir una lista, o una lista ordenada, usando `reverse:
names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)
Las listas también se pueden ordenar utilizando el método Java java.util.Arrays.sort
y su envoltorio Scala scala.util.Sorting.quickSort
java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)
Estos métodos pueden mejorar el rendimiento al ordenar colecciones más grandes si se pueden evitar las conversiones de colección y el desempaquetado / boxeo. Para una discusión más detallada sobre las diferencias de rendimiento, lea acerca de Scala Collection ordenados, sortWith y sortBy Performance .
Crear una lista que contenga n copias de x
Para crear una colección de n
copias de algún objeto x
, use el método de relleno . Este ejemplo crea una List
, pero esto puede funcionar con otras colecciones para las que el fill
tiene sentido:
// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)
Lista y Vector Cheatsheet
Ahora es una práctica recomendada utilizar
Vector
lugar deList
porque las implementaciones tienen un mejor rendimiento Las características de rendimiento se pueden encontrar aquí .Vector
se puede utilizar donde se utiliza laList
.
Creación de listas
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
Tomar 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
Elementos Prependidos
0 :: List(1, 2, 3) // List(0, 1, 2, 3)
Anexar elementos
List(1, 2, 3) :+ 4 // List(1, 2, 3, 4), complexity is O(n)
Unirse (concatenar) listas
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)
Operaciones comunes
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"
Mapa de la colección Cheatsheet
Tenga en cuenta que esto se refiere a la creación de una colección de tipo
Map
, que es distinta del método demap
.
Creación de mapas
Map[String, Int]()
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()
Un mapa puede considerarse una colección de tuples
para la mayoría de las operaciones, donde el primer elemento es la clave y el segundo es el valor.
val l = List(("a", 1), ("b", 2), ("c", 3))
val m = l.toMap // Map(a -> 1, b -> 2, c -> 3)
Obtener 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)
Añadir elemento (s)
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)
Operaciones comunes
En las operaciones donde ocurre una iteración sobre un mapa ( map
, find
, forEach
, etc.), los elementos de la colección son tuples
. El parámetro de función puede usar los accesores de tupla ( _1
, _2
), o una función parcial con un bloque de caso:
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
Mapa y filtro sobre una colección
Mapa
El 'Mapeo' a través de una colección usa la función de map
para transformar cada elemento de esa colección de una manera similar. La sintaxis general es:
val someFunction: (A) => (B) = ???
collection.map(someFunction)
Puede proporcionar una función anónima:
collection.map((x: T) => /*Do something with x*/)
Multiplicando números enteros por dos
// 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)
Filtrar
filter
se utiliza cuando desea excluir o 'filtrar' ciertos elementos de una colección. Al igual que con map
, la sintaxis general toma una función, pero esa función debe devolver un Boolean
:
val someFunction: (a) => Boolean = ???
collection.filter(someFunction)
Puede proporcionar una función anónima directamente:
collection.filter((x: T) => /*Do something that returns a Boolean*/)
Comprobando los números de pares
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)
Más ejemplos de mapas y filtros
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 )
Introducción a las colecciones de Scala
El marco de Scala Collections, según sus autores , está diseñado para ser fácil de usar, conciso, seguro, rápido y universal.
El marco se compone de rasgos de Scala que están diseñados para ser bloques de construcción para crear colecciones. Para obtener más información sobre estos bloques de construcción, lea el resumen oficial de las colecciones de Scala .
Estas colecciones integradas se separan en los paquetes inmutables y mutables. Por defecto, se utilizan las versiones inmutables. Construir una List()
(sin importar nada) construirá una lista inmutable .
Una de las características más poderosas del marco es la interfaz consistente y fácil de usar a través de colecciones afines. Por ejemplo, sumar todos los elementos en una colección es igual para Listas, Conjuntos, Vectores, Seqs y 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
Estos tipos afines heredan del rasgo Traversable
.
Ahora es una práctica recomendada utilizar
Vector
lugar deList
porque las implementaciones tienen un mejor rendimiento Las características de rendimiento se pueden encontrar aquí .Vector
se puede utilizar donde se utiliza laList
.
Tipos transitables
Clases de colección que tienen el Traversable
rasgo implementar foreach
y heredar muchos métodos para realizar operaciones comunes a las colecciones, que todos funcionan de forma idéntica. Las operaciones más comunes se enumeran aquí:
- Mapa -
map
,flatMap
ycollect
producir nuevas colecciones mediante la aplicación de una función a cada elemento de la colección original.
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")
- Conversiones :
toList
,toArray
y muchas otras operaciones de conversión cambian la colección actual en un tipo más específico de colección. Por lo general, estos son métodos prependidos con 'to' y el tipo más específico (es decir, 'toList' se convierte en unaList
).
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
- Información de tamaño :
isEmpty
,nonEmpty
,size
yhasDefiniteSize
son todos metadatos sobre el conjunto. Esto permite operaciones condicionales en la colección, o para que el código determine el tamaño de la colección, incluso si es infinito o discreto.
List().isEmpty // true
List(1).nonEmpty // true
- Recuperación de elementos :
head
,last
,find
y sus variantes deOption
se utilizan para recuperar el primer o último elemento, o encontrar un elemento específico en la colección.
val list = List(1, 2, 3)
list.head // = 1
list.last // = 3
- Operaciones de recuperación de subcolecciones: las operaciones de
filter
,tail
,slice
,drop
y otras permiten elegir partes de la colección para seguir operando.
List(-2, -1, 0, 1, 2).filter(num => num > 0) // = List(1, 2)
- Operaciones de subdivisión :
partition
,splitAt
,span
ygroupBy
dividen la colección actual en partes diferentes.
// split numbers into < 0 and >= 0
List(-2, -1, 0, 1, 2).partition(num => num < 0) // = (List(-2, -1), List(0, 1, 2))
- Pruebas de elementos -
exists
,forall
, ycount
se utilizan operaciones de control de esta colección para ver si satisface un predicado.
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
yreduceRight
se utilizan para aplicar funciones binarias a elementos sucesivos de la colección. Vaya aquí para ver ejemplos de pliegues e ir aquí para ejemplos de reducción
Doblez
El método de fold
recorre una colección, utilizando un valor inicial del acumulador y aplicando una función que utiliza cada elemento para actualizar el acumulador con éxito:
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
En el ejemplo anterior, se proporcionó una función anónima para fold()
. También puede usar una función nombrada que toma dos argumentos. Teniendo esto en mi, el ejemplo anterior se puede reescribir así:
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
Cambiar el valor inicial afectará el resultado:
initialValue = 2;
sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17
El método de fold
tiene dos variantes: foldLeft
y foldRight
.
foldLeft()
itera de izquierda a derecha (desde el primer elemento de la colección hasta el último en ese orden). foldRight()
itera de derecha a izquierda (desde el último elemento hasta el primer elemento). fold()
itera de izquierda a derecha como foldLeft()
. De hecho, fold()
realidad llama a foldLeft()
internamente.
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
fold()
, foldLeft()
y foldRight()
devolverá un valor que tiene el mismo tipo con el valor inicial que toma. Sin embargo, a diferencia de foldLeft()
y foldRight()
, el valor inicial dado para fold()
solo puede ser del mismo tipo o un supertipo del tipo de la colección.
En este ejemplo, el orden no es relevante, por lo que puede cambiar fold()
por foldLeft()
o foldRight()
y el resultado seguirá siendo el mismo. El uso de una función que sea sensible al orden alterará los resultados.
En caso de duda, prefiera foldLeft()
sobre foldRight()
. foldRight()
tiene menos rendimiento.
Para cada
foreach
es inusual entre los iteradores de colecciones porque no devuelve un resultado. En su lugar, aplica una función a cada elemento que tiene solo efectos secundarios. Por ejemplo:
scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> x.foreach { println }
1
2
3
La función suministrada a foreach
puede tener cualquier tipo de retorno, pero el resultado se descartará . Normalmente se usa foreach
cuando los efectos secundarios son deseables. Si desea transformar los datos, considere usar un map
, un filter
, una for comprehension
u otra opción.
Ejemplo de descarte de resultados.
def myFunc(a: Int) : Int = a * 2
List(1,2,3).foreach(myFunc) // Returns nothing
Reducir
Los métodos reduce()
, reduceLeft()
y reduceRight
son similares a los pliegues. La función pasada para reducir toma dos valores y produce un tercero. Cuando se opera en una lista, los dos primeros valores son los dos primeros valores de la lista. El resultado de la función y el siguiente valor en la lista se vuelven a aplicar a la función, produciendo un nuevo resultado. Este nuevo resultado se aplica con el siguiente valor de la lista y así sucesivamente hasta que no haya más elementos. Se devuelve el resultado final.
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
Existen algunas diferencias en cómo funcionan las funciones de reducción en comparación con las funciones de plegado. Son:
- Las funciones de reducción no tienen valor acumulador inicial.
- Las funciones de reducción no se pueden llamar en listas vacías.
- Las funciones de reducción solo pueden devolver el tipo o supertipo de la lista.