¿Es posible usar una llamada a collect
para hacer 2 listas nuevas? Si no, ¿cómo puedo hacer esto usando partition
?Scala Partition/Collect Usage
Respuesta
collect
(definida en TraversableLike y está disponible en todas las subclases) funciona con una colección y PartialFunction
. También sucede que un montón de cláusulas de casos definidos dentro de llaves son una función parcial (Véase la sección 8.5 del Scala Language Specification[advertencia - PDF])
Al igual que en el manejo de excepciones:
try {
... do something risky ...
} catch {
//The contents of this catch block are a partial function
case e: IOException => ...
case e: OtherException => ...
}
que es una forma práctica de definir una función que solo aceptará algunos valores de un tipo dado.
Considere el uso en una lista de valores mixtos:
val mixedList = List("a", 1, 2, "b", 19, 42.0) //this is a List[Any]
val results = mixedList collect {
case s: String => "String:" + s
case i: Int => "Int:" + i.toString
}
El argumento de que es un método collect
PartialFunction[Any,String]
. PartialFunction
porque no está definido para todas las entradas posibles de tipo Any
(que es el tipo de List
) y String
porque eso es lo que devuelven todas las cláusulas.
Si ha intentado utilizar map
en lugar de collect
, el valor doble al final de mixedList
causaría un MatchError
. El uso de collect
simplemente descarta esto, así como cualquier otro valor para el cual PartialFunction no está definido.
Un posible uso sería aplicar lógica diferente a los elementos de la lista:
var strings = List.empty[String]
var ints = List.empty[Int]
mixedList collect {
case s: String => strings :+= s
case i: Int => ints :+= i
}
Aunque esto es sólo un ejemplo, el uso de variables mutables como esto es considerado por muchos como un crimen de guerra - Así que por favor no lo hagas!
Una gran solución mejor es utilizar recoger dos veces:
val strings = mixedList collect { case s: String => s }
val ints = mixedList collect { case i: Int => i }
O si usted sabe con certeza que la lista sólo contiene dos tipos de valores, puede utilizar partition
, que divide a las colecciones en valores dependiendo de si son o no coinciden con algún predicado:
//if the list only contains Strings and Ints:
val (strings, ints) = mixedList partition { case s: String => true; case _ => false }
El truco aquí es que tanto strings
y ints
son de tipo.210, aunque se puede coaccionar fácilmente de vuelta a algo más typesafe (tal vez mediante el uso de collect
...)
Si ya tiene una colección typesafe y quieren dividir en alguna otra propiedad de los elementos, entonces las cosas son un poco más fácil para usted:
val intList = List(2,7,9,1,6,5,8,2,4,6,2,9,8)
val (big,small) = intList partition (_ > 5)
//big and small are both now List[Int]s
¡Espero que resuma cómo los dos métodos pueden ayudarle aquí!
No está seguro de cómo hacerlo con collect
sin usar listas mutables, pero partition
puede utilizar coincidencia de patrones, así (sólo un poco más detallado)
List("a", 1, 2, "b", 19).partition {
case s:String => true
case _ => false
}
@coubeatczech - Debido partición devuelve una '(Lista [A], Lista [A])'. Eso es todo lo que puede hacer dado que la entrada es una 'Lista [A]' y una función indicadora 'A => Booleana'. No tiene forma de saber que la función del indicador podría ser específica del tipo. –
@Rex Definí mi propio método de 'cotejo' para proxenetismo en colecciones que resuelven justamente este problema. En una 'Lista [A]' la firma del caso de uso es 'intercalar [B] (fn: Función parcial [A, B]): (Lista (B), Lista (A))', obviamente la firma * real * es un poco más peludo que eso ya que también estoy usando 'CanBuildFrom' –
La firma del collect
utilizado normalmente en, por ejemplo, Seq
, es
collect[B](pf: PartialFunction[A,B]): Seq[B]
que en realidad es un caso particular de
collect[B, That](pf: PartialFunction[A,B])(
implicit bf: CanBuildFrom[Seq[A], B, That]
): That
Así que si lo usa en el modo por defecto, el la respuesta es no, ciertamente no: obtienes exactamente una secuencia de ella. Si sigue CanBuildFrom
hasta Builder
, verá que sería posible hacer que That
sean en realidad dos secuencias, pero no tendría forma de saber a qué secuencia debe ir un elemento, ya que la función parcial solo puede decir "sí, yo" pertenecer "o" no, no pertenezco ".
Entonces, ¿qué haces si quieres tener varias condiciones que hacen que tu lista se divida en varias piezas diferentes? Una forma es crear una función de indicador A => Int
, donde su A
esté mapeado en una clase numerada, y luego use groupBy
. Por ejemplo:
def optionClass(a: Any) = a match {
case None => 0
case Some(x) => 1
case _ => 2
}
scala> List(None,3,Some(2),5,None).groupBy(optionClass)
res11: scala.collection.immutable.Map[Int,List[Any]] =
Map((2,List(3, 5)), (1,List(Some(2))), (0,List(None, None)))
Ahora usted puede buscar sus sublistas por clase (0, 1 y 2 en este caso). Desafortunadamente, si quiere ignorar algunas entradas, igual debe ponerlas en una clase (por ejemplo, probablemente no le importen las copias múltiples de None
en este caso).
Yo uso esto. Una cosa buena de esto es que combina partición y mapeo en una iteración. Una desventaja es que asignar un montón de objetos temporales (los Either.Left
y Either.Right
casos)
/**
* Splits the input list into a list of B's and a list of C's, depending on which type of value the mapper function returns.
*/
def mapSplit[A,B,C](in: List[A])(mapper: (A) => Either[B,C]): (List[B], List[C]) = {
@tailrec
def mapSplit0(in: List[A], bs: List[B], cs: List[C]): (List[B], List[C]) = {
in match {
case a :: as =>
mapper(a) match {
case Left(b) => mapSplit0(as, b :: bs, cs )
case Right(c) => mapSplit0(as, bs, c :: cs)
}
case Nil =>
(bs.reverse, cs.reverse)
}
}
mapSplit0(in, Nil, Nil)
}
val got = mapSplit(List(1,2,3,4,5)) {
case x if x % 2 == 0 => Left(x)
case y => Right(y.toString * y)
}
assertEquals((List(2,4),List("1","333","55555")), got)
no pude encontrar una solución satisfactoria a este problema básico aquí. No necesito una conferencia en collect
y no me importa si esta es la tarea de alguien. Además, no quiero algo que funcione solo para List
.
Así que aquí está mi puñalada. Eficientes y compatible con cualquier TraversableOnce
, incluso cadenas:
implicit class TraversableOnceHelper[A,Repr](private val repr: Repr)(implicit isTrav: Repr => TraversableOnce[A]) {
def collectPartition[B,Left](pf: PartialFunction[A, B])
(implicit bfLeft: CanBuildFrom[Repr, B, Left], bfRight: CanBuildFrom[Repr, A, Repr]): (Left, Repr) = {
val left = bfLeft(repr)
val right = bfRight(repr)
val it = repr.toIterator
while (it.hasNext) {
val next = it.next
if (!pf.runWith(left += _)(next)) right += next
}
left.result -> right.result
}
def mapSplit[B,C,Left,Right](f: A => Either[B,C])
(implicit bfLeft: CanBuildFrom[Repr, B, Left], bfRight: CanBuildFrom[Repr, C, Right]): (Left, Right) = {
val left = bfLeft(repr)
val right = bfRight(repr)
val it = repr.toIterator
while (it.hasNext) {
f(it.next) match {
case Left(next) => left += next
case Right(next) => right += next
}
}
left.result -> right.result
}
}
Ejemplo Usos:
val (syms, ints) =
Seq(Left('ok), Right(42), Right(666), Left('ko), Right(-1)) mapSplit identity
val ctx = Map('a -> 1, 'b -> 2) map {case(n,v) => n->(n,v)}
val (bound, unbound) = Vector('a, 'a, 'c, 'b) collectPartition ctx
println(bound: Vector[(Symbol, Int)], unbound: Vector[Symbol])
- 1. Stanford Parser multithread usage
- 2. CaptiveNetwork: CNSetSupportedSSIDs usage
- 3. std :: this_thread :: yield() usage?
- 4. CPU Usage y Object.wait
- 5. MyBatis enum usage
- 6. C# memory usage
- 7. Spring @Autowired usage
- 8. Energy Usage Instruments - iOS
- 9. OpenGL Video Memory Usage
- 10. Python Slice Assignment Memory Usage
- 11. Django admin list_display property usage
- 12. Artificial Limit C/C++ Memory Usage
- 13. <AppName> .pch file usage
- 14. Scala StringBuilder
- 15. ASP.NET MVC y EF Code First Memory Usage
- 16. Scala AST en Scala
- 17. Objeto Scala scala
- 18. Evitar pérdidas de memoria Scala - Scala constructores
- 19. Scala SBT: Scala versión del compilador
- 20. scala reflection
- 21. Scala constructor
- 22. Empresa Scala
- 23. Scala Profiler?
- 24. Scala profiler?
- 25. Scala @ operator
- 26. ¿Por qué un rasgo de Scala puede extender una clase?
- 27. Error Scala IDE - "projectname" no es un proyecto de Scala -
- 28. Pregunta sobre el cierre de Scala (de "Programación en Scala")
- 29. ¿Cómo obtengo la versión de Scala dentro de Scala?
- 30. Java <-> Conversiones de Scala Collection, Scala 2.10
Muy buena explicación, pero lo que creo que OP quiere es una combinación de' collect' y 'partition' que devuelve una tupla de una lista de los valores recopilados y una lista de todo el resto. 'def collectAndPartition [A, B] (pf: PartialFunction [A, B]): (Lista [B], Lista [A])'. Esto probablemente se lograría de forma más elegante con una función de biblioteca nativa, es decir, en la fuente de 'collect' en TraversableLike tenemos' for (x <- this) if (pf.isDefinedAt (x)) b + = pf (x) ' , uno podría tachar simplemente un 'else a + = x' al final de eso, donde' a' sería un constructor para la lista de todos los demás. –
Sé lo que necesita el OP, y también soy consciente de que esta es una pregunta para la tarea (se ha mencionado mucho en el desbordamiento de pila recientemente), así que con mucho gusto daré un montón de teoría sin resolverla en realidad. En cuanto a collectAndPartition, ya escribí eso, aunque nombré el método 'collate'.Si alguien está enseñando scala al nivel en el que se espera que los estudiantes trabajen con CanBuildFrom, entonces me sorprenderá mucho, está más allá de la mayoría de las personas que actualmente usan scala en producción. –
Eso fue muy útil. Pero todavía estoy pensando ... ¿es posible separar, por ejemplo, los valores positivos y negativos, sin hacer un "crimen de guerra" como escribiste antes? Solo soy cómico porque ya hice la tarea con el uso de la partición. Ohhh ... Y, por cierto, ¡gracias por la ayuda! –