retour scala sur la première partie de la liste
-
30-09-2019 - |
Question
J'ai une l:List[T1]
liste et faire actuellement im ce qui suit:
myfun : T1 -> Option[T2]
val x: Option[T2] = l.map{ myfun(l) }.flatten.find(_=>true)
La fonction retourne myfun
Aucun ou Certains, aplatissez jette tout est le Néant et trouver renvoie le premier élément de la liste le cas échéant.
Cela semble un peu hacky pour moi. Im pensant qu'il pourrait exister une certaine compréhension pour-ou similaire qui le fera un peu moins de gaspillage ou plus intelligent.
Par exemple: Je ne demande aucune réponse ultérieure si retourne myfun
any Some
pendant la map
de la liste l
La solution
Que diriez-vous:
l.toStream flatMap (myfun andThen (_.toList)) headOption
Stream est paresseux, il ne sera pas la carte tout à l'avance, mais il ne sera pas les choses Remapper non plus. Au lieu d'aplanir les choses, convertir Option
à List
afin que flatMap
peut être utilisé.
Autres conseils
Eh bien, cela est presque, mais pas tout à fait
val x = (l flatMap myfun).headOption
Mais vous retournez un Option
plutôt que d'un List
de myfun, donc cela ne fonctionne pas. Si oui (je n'ai pas REPL à la main) puis essayez à la place:
val x = (l flatMap(myfun(_).toList)).headOption
En plus d'utiliser toStream
pour faire la recherche paresseuse, nous pouvons utiliser 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
-
Transforme le
List
dans unStream
afin d'arrêter la recherche précoce. -
éléments Transforms utilisant
myFun
commeOption[T]
s. -
Collects le premier élément mappé qui ne
None
et l'extraire.
À partir Scala 2.13
, la dévalorisation de Stream
s en faveur de LazyList
s, cela deviendrait:
List(1, 2, 3, 4, 5, 6, 7, 8).to(LazyList).map(function).collectFirst { case Some(d) => d }
Eh bien, le est assez facile équivalent-compréhension
(for(x<-l, y<-myfun(x)) yield y).headOption
qui, si vous le faites en fait la traduction fonctionne la même chose que ce oxbow_lakes a donné. En supposant que la paresse raisonnable List.flatmap, c'est à la fois une solution propre et efficace.
En 2017, les réponses précédentes semblent être dépassées. J'ai couru quelques repères (liste de 10 millions Ints, d'abord correspondre à peu près au milieu, Scala 2.12.3, Java 1.8.0, 1,8 GHz Intel Core i5). Sauf indication contraire, list
et map
ont les types suivants:
list: scala.collection.immutable.List
map: A => Option[B]
Il suffit d'appeler map
sur la liste: 1000 ~ ms
list.map(map).find(_.isDefined).flatten
Premier appel toStream
sur la liste: 1200 ~ ms
list.toStream.map(map).find(_.isDefined).flatten
Appel toStream.flatMap
sur la liste: ~ 450 ms
list.toStream.flatMap(map(_).toList).headOption
Appel flatMap
sur la liste: ~ 100 ms
list.flatMap(map(_).toList).headOption
Premier appel iterator
sur la liste: ~ 35 ms
list.iterator.map(map).find(_.isDefined).flatten
fonction récursive 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
}
}
}
Fonction itératives 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
}
Vous pouvez encore accélérer les choses en utilisant Java au lieu des collections Scala et un style moins fonctionnel.
Boucle sur indices 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
}
Boucle sur indices java.util.ArrayList
avec fonction retour null
au lieu 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
}
(Bien sûr, on déclarerions généralement le type de paramètre comme java.util.List
, non java.util.ArrayList
j'ai choisi ce dernier ici parce que c'est la classe j'ai utilisé pour les points de repère D'autres implémentations de java.util.List
montreront des performances différentes -.. La plupart sera pire.)