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

.
Était-ce utile?

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 un Stream afin d'arrêter la recherche précoce.

  • éléments Transforms utilisant myFun comme Option[T]s.

  • Collects le premier élément mappé qui ne None et l'extraire.

À partir Scala 2.13, la dévalorisation de Streams en faveur de LazyLists, 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.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top