2010-09-04 10 views
12

tengo una lista l:List[T1] y actualmente estoy haciendo lo siguiente:retorno Scala el primer Algunos en la lista

myfun : T1 -> Option[T2] 
val x: Option[T2] = l.map{ myfun(l) }.flatten.find(_=>true) 

La función myfun devuelve None o Algunos, aplanar tira a la basura todo el Ninguno de y encontrar devuelve el primer elemento de la lista si la hay

Esto me parece un poco raro. Estoy pensando que podría existir alguna comprensión forzosa o similar que lo haga un poco menos derrochador o más inteligente. Por ejemplo: no necesito ninguna respuesta posterior si myfun devuelve cualquierSome durante el map de la lista l.

Respuesta

12

¿Qué tal:

l.toStream flatMap (myfun andThen (_.toList)) headOption 

Stream es lento, por lo que no será mapear todo por adelantado, pero no va a volver a asignar las cosas bien. En lugar de aplanar las cosas, convierta Option en List para que se pueda usar flatMap.

+1

Si no me confundo, también se puede usar "flatMap" en Option, así que creo "y luego (_.toList) "es superfluo –

+0

@Jens si lo prueba, verá que no funciona. –

+1

val l = Lista (1, 2, 3, 4, 5, 6) diversión def (i: int) = { si (i == 3) Algunos (3) otro Ninguno } println (l .flatMap (fun (_)). head) println (l.flatMap (fun (_)). headOption) –

3

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, por lo que este puede no funcionar. Si es así (no tengo REPL a mano) a continuación, intente en su lugar:

val x = (l flatMap(myfun(_).toList)).headOption 
2

Pues bien, el de la comprensión-equivalente es bastante fácil

(for(x<-l, y<-myfun(x)) yield y).headOption 

la que, si realmente hace lo resuelve la traducción lo mismo que lo que dio oxbow_lakes. Asumiendo una pereza razonable de List.flatmap, esta es una solución limpia y eficiente.

+2

Desafortunadamente, Lista (es decir collection.immutable.List) no tiene operaciones perezosas. Por razones que no entiendo, reemplazar 'l' con' l.view' resultados en myfun se evalúan varias veces con los mismos argumentos. –

+7

La vista es llamada por nombre, no floja. Si desea una evaluación a lo sumo, use toStream: (l.toStream flatMap myfun) .headOption –

0

A partir de 2017, las respuestas anteriores parecen estar desactualizadas. Ejecuté algunos puntos de referencia (lista de 10 millones de Ints, primer partido aproximadamente en el medio, Scala 2.12.3, Java 1.8.0, Intel Core i5 de 1.8 GHz).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 

Llame toStream.flatMap en la lista: ~ 450 ms

list.toStream.flatMap(map(_).toList).headOption 

llamada flatMap 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 

función recursiva 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 
    } 
    } 
} 

Función iterativa 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 
} 

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 los índices de 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 por lo general declare el tipo de parámetro como java.util.List, no java.util.ArrayList. Escogí este último aquí porque es la clase que utilicé para los puntos de referencia. Otras implementaciones de java.util.List mostrará un rendimiento diferente; la mayoría será peor).

+0

Perfilar el JVM es [notoriamente problemático] (https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java). ¿Qué herramientas de referencia usaste? [Tomillo] (https://github.com/Ichoran/thyme)? JMH? ¿Otro? – jwvh

+0

Conozco los problemas con los puntos de referencia de Java. No usé ninguna herramienta, solo System.nanoTime(). Cada uno de estos números es el promedio de 100 ejecuciones: inicie JVM, complete la lista con 10 millones de entradas aleatorias, inicie el reloj, ejecute find() 100 veces, pare el reloj. No es muy preciso, pero dado que existen diferencias de varios órdenes de magnitud, diría que este simple punto de referencia ofrece al menos una descripción aproximada útil del rendimiento relativo de estos enfoques. –

+0

Estoy viajando ahora, pero espero tener tiempo para cargar el código (muy simple) que usé en los próximos días. ¡Me encantaría ver los mismos puntos de referencia medidos con una herramienta adecuada! –

Cuestiones relacionadas