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

.
¿Fue útil?

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 un Stream con el fin de detener la búsqueda antes de tiempo.

  • Transforma elementos usando myFun como Option[T]s.

  • recoge el primer elemento de mapeado que no es None y extraerlo.

A partir Scala 2.13, con la desaprobación de Streams en favor de LazyLists, 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.)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top