2010-04-03 9 views
5

Editar: Se agregó el hecho de que la lista está ordenada, y darse cuenta de que 'duplicado' es engañoso, reemplazó eso con 'redundante' en el título.Eliminar entradas redundantes, scala way

Tengo una lista ordenada de entradas que indican un valor de producción en un intervalo determinado. Las entradas que indican el mismo valor en un momento posterior no agregan información y pueden omitirse de manera segura.

case class Entry(minute:Int, production:Double) 
val entries = List(Entry(0, 100.0), Entry(5, 100.0), Entry(10, 100.0), Entry(20, 120.0), Entry(30, 100.0), Entry(180, 0.0)) 

Experimentando con las funciones de recaudación Scala 2.8, hasta ahora tengo esta implementación trabajo:

entries.foldRight(List[Entry]()) { 
    (entry, list) => list match { 
    case head :: tail if (entry.production == head.production) => entry :: tail 
    case head :: tail => entry :: list 
    case List() => entry :: List() 
    } 
} 
res0: List[Entry] = List(Entry(0,100.0), Entry(20,120.0), Entry(30,100.0), Entry(180,0.0)) 

Cualquier comentario? ¿Me estoy perdiendo algo de magia scala?

+0

Eso sí, 'foldRight' no es óptimo con' List'. Prefiero 'foldLeft' con él.Esto es lo opuesto a 'Haskell', donde' Right' se prefiere a 'Left' debido a la falta de rigor. –

+0

bien, pero luego necesito revertir el resultado. Ejecutar una prueba rápida coloca foldRight ligeramente por delante de foldLeft + reverse, por lo que diría foldRight es más claro. – andersbohn

Respuesta

9

Al comparar las entradas consecutivas en una lista, comience por zip -siguiendo la lista con su cola para obtener una lista de pares de elementos consecutivos.

A continuación, tomo la primera entrada de la lista, y uso collect para filtrar simultáneamente pares donde la producción no se modifica, y para los pares restantes, mapa e2. (collect es nuevo en Scala 2.8, y por un tiempo se llamó partialMap)

scala> entries.head :: ((entries zip entries.tail).collect { 
      case (Entry(_, p1), [email protected](_, p2)) if p1 != p2 => e2 
     }) 
res13: List[Entry] = List(Entry(0,100.0), Entry(20,120.0), Entry(30,100.0), Entry(180,0.0)) 

ACTUALIZACIÓN Para simplificar, esto supone que las entradas no está vacío.

+1

muy buena idea general, relampagueando con la cola. Es algo más lento que foldright. x2 en mi configuración (2.8.0.Beta1-RC3, donde recopilar sigue siendo 'partialMap', no sé si eso afecta el rendimiento) – andersbohn

+1

@andersbohn Puede usar 'entries.view zip entries.tail' para obtener un mejor rendimiento de él ('.toList' al final), pero mis pruebas ponen su versión en 30,' view' en 63 y retronym en 81. –

0

En lugar de buscar duplicados para cada elemento, que es O (n^2), o comprimir, que es n^2 en la memoria, utilice el mapa [Doble, Int]. Luego solo agregue los elementos con la 'producción' como la clave y el 'minuto' como el valor. El mapa asegurará valores únicos para 'producción'. Es posible que pueda cargar el mapa de forma natural en cualquier parte de su código, pero incluso si tiene que comenzar con la lista anterior, cargar el mapa es lineal en la lista y solo O (n log (n)) en el mapa.

El mapa se reemplazará a medida que agrega "mymap + = production -> minute", de modo que para conservar el primer valor, invierta la lista antes de insertar o use un protector 'contains (key)'. Las comprobaciones serán O (log (n)) por lo que el algoritmo general será O (n log (n)).

Por cierto, podría usar un mapa [Entrada doble] para asignar los valores de producción directamente a sus estructuras de Entrada. A continuación, puede obtener fácilmente una lista, si es necesario, extrayendo los valores del mapa directamente del mapa y clasificando cualquiera de los elementos de la Entrada (si es necesario).

+0

Creo que está malinterpretando. Andersbohn solo necesita ir una vez a la lista; ya está ordenado, y si aparece una producción, cambia y luego vuelve a cambiar, necesita _do_ la nueva producción. (El punto es simplemente tirar todo lo que ya está haciendo como redundante.) Tanto el código de retronym como el de andersbohn son 'O (n)'; pasan una vez a través de los datos. –

+0

Quizás; No creo que la pregunta original fuera tan específica. Con suerte, mi respuesta será útil para otros con preguntas similares. Además, buscar en toda la lista cada vez hace que el algoritmo O (n^2) en el número de elementos. Eso se puede mejorar con una estructura hashtable o árbol. – DrGary

+0

Si hubiera dicho algo sobre las actualizaciones O (log n), tal vez estaría de acuerdo. De lo contrario, ¿por qué usar un mapa cuando puede ordenar O (n log n) y luego eliminar duplicados en O (n)? –

3

Hay un nuevo método zipped con Tuple2 que es más eficiente (y más lento) que zip en las listas para algunas operaciones. Es posible probar esto en su punto de referencia - No sé si es realmente más rápido, pero sin duda podría ser (y es definitivamente mucho más corto):

entries.take(1) ::: 
(entries,entries.drop(1)).zipped.filter(_.production != _.production)._2 

En lugar de comprimir la lista de pares de todo En el camino, crea una vista de la lista donde las piezas se pueden manipular juntas, y luego regresa las listas manipuladas. Tenga en cuenta el uso de tomar y soltar para manejar la caja vacía.

No es súper eficiente ya que crea dos listas cuando realmente solo necesita una, pero es una clase de solución que aún no ha aparecido.

Cuestiones relacionadas