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

.
War es hilfreich?

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 eine Stream, um frühzeitig die Suche zu beenden.

  • Wandeln Elemente mit myFun als Option[T]s.

  • Fängt das erste zugeordnete Element, das nicht None ist und es extrahieren.

Starten Scala 2.13, mit der Geringschätzung von Streams für LazyLists, 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.)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top