scala Rückkehr auf den ersten Teil in der Liste
-
30-09-2019 - |
Frage
Ich habe eine Liste l:List[T1]
und zur Zeit im tat die folgenden:
myfun : T1 -> Option[T2]
val x: Option[T2] = l.map{ myfun(l) }.flatten.find(_=>true)
Die myfun
Funktion gibt keine oder Einige, Flatten wegwirft alle keine der und finden gibt das erste Element der Liste, falls vorhanden.
Dies scheint ein wenig hacky zu mir. Im denkend, dass es vielleicht etwas für Verständnis oder ähnliches bestehen, dass dies ein tut etwas weniger verschwenderisch oder klüger.
Zum Beispiel: Ich brauche alle nachfolgenden Antworten nicht, wenn myfun
kehrt jeder Some
während des map
der Liste l
Lösung
Wie wäre:
l.toStream flatMap (myfun andThen (_.toList)) headOption
Strom ist faul, so dass es nicht alles im Voraus abbildet, aber es wird nicht remap Dinge auch nicht. Statt Dinge Abflachung, convert Option
zu List
so dass flatMap
verwendet werden kann.
Andere Tipps
Nun, das ist fast, aber nicht ganz
val x = (l flatMap myfun).headOption
Aber Sie sind wieder ein Option
eher als ein List
von myfun, so dass dies möglicherweise nicht. Wenn ja (ich habe keine REPL Hand), dann versuchen Sie stattdessen:
val x = (l flatMap(myfun(_).toList)).headOption
Neben toStream
mit der Suche faul zu machen, können wir 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
Dieses:
-
Wandelt die
List
in eineStream
, um frühzeitig die Suche zu beenden. -
Wandeln Elemente mit
myFun
alsOption[T]
s. -
Fängt das erste zugeordnete Element, das nicht
None
ist und es extrahieren.
Starten Scala 2.13
, mit der Geringschätzung von Stream
s für LazyList
s, dies würde:
List(1, 2, 3, 4, 5, 6, 7, 8).to(LazyList).map(function).collectFirst { case Some(d) => d }
Nun, die für Verständnis entspricht ziemlich einfach
(for(x<-l, y<-myfun(x)) yield y).headOption
, die, wenn Sie das wirklich tun die Übersetzung funktioniert das gleiche wie das, was oxbow_lakes gab. Unter der Annahme, vernünftige Faulheit von List.flatmap, ist dies sowohl eine saubere und effiziente Lösung.
Ab 2017 scheinen die bisherigen Antworten überholt zu sein. Ich lief einige Benchmarks (Liste von 10 Millionen Ints, passen zunächst grob in der Mitte, Scala 2.12.3, Java 1.8.0, 1.8 GHz Intel Core i5). Wenn nicht anders angegeben, list
und map
die folgenden Typen haben:
list: scala.collection.immutable.List
map: A => Option[B]
Simply map
auf der Liste aufrufen: ~ 1000 ms
list.map(map).find(_.isDefined).flatten
Erster Aufruf toStream
auf der Liste: ~ 1200 ms
list.toStream.map(map).find(_.isDefined).flatten
Anruf toStream.flatMap
auf der Liste: ~ 450 ms
list.toStream.flatMap(map(_).toList).headOption
Anruf flatMap
auf der Liste: ~ 100 ms
list.flatMap(map(_).toList).headOption
Erster Aufruf iterator
auf der Liste: ~ 35 ms
list.iterator.map(map).find(_.isDefined).flatten
rekursive Funktion 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
}
}
}
Iterative Funktion 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
}
Sie können die Dinge weiter beschleunigen, indem sie mit Hilfe von Java anstelle von Scala Sammlungen und weniger funktional.
Schleife über Indizes in 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
}
Schleife über Indizes in java.util.ArrayList
mit Funktion null
statt None
Rückkehr: ~ 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
}
(Natürlich würde man in der Regel den Parametertyp als java.util.List
erklären, nicht java.util.ArrayList
ich das letztere hier gewählt, weil es die Klasse, das ich für die Benchmarks Andere Implementierungen von java.util.List
unterschiedliche Performance zeigen wird -.. Die meisten schlechter sein wird.)