retorno Scala el primer Algunos en la lista
-
30-09-2019 - |
Pregunta
Tengo un l:List[T1]
lista y actualmente estoy haciendo lo siguiente:
myfun : T1 -> Option[T2]
val x: Option[T2] = l.map{ myfun(l) }.flatten.find(_=>true)
La función devuelve myfun
Ninguno o Algunos, aplanar tira a la basura todo el Ninguno de encontrar y devuelve el primer elemento de la lista, si los hubiere.
Esto parece un poco hacky para mí. Im pensando que podría existir alguna para la comprensión o similar que lo hará un poco menos derrochador o más inteligente.
Por ejemplo: No necesito ninguna respuesta posteriores si los rendimientos myfun
cualquier Some
durante el map
de la lista l
Solución
¿Qué hay de:
l.toStream flatMap (myfun andThen (_.toList)) headOption
Stream es lento, por lo que no será mapear todo por adelantado, pero no va a cosas de reasignación tampoco. En lugar de aplanar las cosas, Option
convertido al List
modo que flatMap
se puede utilizar.
Otros consejos
Bueno, esto es casi, pero no del todo
val x = (l flatMap myfun).headOption
Pero se están volviendo un Option
en lugar de un List
de myfun, así que esto no funcione. Si es así (no tengo REPL a mano) a continuación, intente en su lugar:
val x = (l flatMap(myfun(_).toList)).headOption
Además de utilizar toStream
para hacer la búsqueda perezoso, podemos usar Stream::collectFirst
:
List(1, 2, 3, 4, 5, 6, 7, 8).toStream.map(myfun).collectFirst { case Some(d) => d }
// Option[String] = Some(hello)
// given def function(i: Int): Option[String] = if (i == 5) Some("hello") else None
Este:
-
transforma el
List
en unStream
con el fin de detener la búsqueda antes de tiempo. -
Transforma elementos usando
myFun
comoOption[T]
s. -
recoge el primer elemento de mapeado que no es
None
y extraerlo.
A partir Scala 2.13
, con la desaprobación de Stream
s en favor de LazyList
s, esto se convertiría en:
List(1, 2, 3, 4, 5, 6, 7, 8).to(LazyList).map(function).collectFirst { case Some(d) => d }
Bueno, el equivalente para la comprensión es bastante fácil
(for(x<-l, y<-myfun(x)) yield y).headOption
cual, si usted hace realmente el la traducción funciona lo mismo que lo dieron oxbow_lakes. Suponiendo pereza razonable de List.flatmap, esto es a la vez una solución limpia y eficiente.
A partir de 2017, las respuestas anteriores parecen ser obsoleta. Me encontré con algunos puntos de referencia (lista de 10 millones de intercepciones, primero coincidir más o menos en el medio, Scala 2.12.3, Java 1.8.0, 1.8 GHz Intel Core i5). A menos que se indique lo contrario, list
y map
tienen los siguientes tipos:
list: scala.collection.immutable.List
map: A => Option[B]
Simplemente llame map
en la lista: ~ 1000 ms
list.map(map).find(_.isDefined).flatten
Primera llamada toStream
en la lista: ~ 1200 ms
list.toStream.map(map).find(_.isDefined).flatten
toStream.flatMap
de llamadas en la lista: ~ 450 ms
list.toStream.flatMap(map(_).toList).headOption
flatMap
de llamadas en la lista: ~ 100 ms
list.flatMap(map(_).toList).headOption
Primera llamada iterator
en la lista: ~ 35 ms
list.iterator.map(map).find(_.isDefined).flatten
recursiva función find()
: ~ 25 ms
def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = {
list match {
case Nil => None
case head::tail => map(head) match {
case None => find(tail, map)
case result @ Some(_) => result
}
}
}
iterativo función find()
: ~ 25 ms
def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = {
for (elem <- list) {
val result = map(elem)
if (result.isDefined) return result
}
return None
}
Se puede acelerar aún más las cosas mediante el uso de Java en lugar de colecciones Scala y un estilo menos funcional.
bucle sobre índices en java.util.ArrayList
: ~ 15 ms
def find[A,B](list: java.util.ArrayList[A], map: A => Option[B]) : Option[B] = {
var i = 0
while (i < list.size()) {
val result = map(list.get(i))
if (result.isDefined) return result
i += 1
}
return None
}
bucle sobre índices en java.util.ArrayList
con función que devuelve null
en lugar de None
: ~ 10 ms
def find[A,B](list: java.util.ArrayList[A], map: A => B) : Option[B] = {
var i = 0
while (i < list.size()) {
val result = map(list.get(i))
if (result != null) return Some(result)
i += 1
}
return None
}
(Por supuesto, uno podría por lo general declaran el tipo de parámetro como java.util.List
, no java.util.ArrayList
Elegí la segunda aquí porque es la clase que he usado para los puntos de referencia Otras implementaciones de java.util.List
mostrarán un rendimiento diferente -.. La mayoría será peor.)