2010-12-27 9 views
5

Estoy tratando de reducir el grado de escritura de Scala (2.8) como Java. Aquí hay una simplificación de un problema que encontré. ¿Puede sugerir mejoras en mis soluciones que son "más funcionales"?Scala, hacer que mi ciclo sea más funcional

Transformar el mapa

val inputMap = mutable.LinkedHashMap(1->'a',2->'a',3->'b',4->'z',5->'c') 

descartando cualquier datos con el valor de 'z' y la indexación de los personajes a medida que se encuentran

En primer lugar tratar

var outputMap = new mutable.HashMap[Char,Int]() 
var counter = 0 
for(kvp <- inputMap){ 
    val character = kvp._2 
    if(character !='z' && !outputMap.contains(character)){ 
    outputMap += (character -> counter) 
    counter += 1 
    } 
} 

segundo intento (no mucho mejor , pero usa un mapa inmutable y un 'foreach')

var outputMap = new immutable.HashMap[Char,Int]() 
var counter = 0 
inputMap.foreach{ 
    case(number,character) => { 
    if(character !='z' && !outputMap.contains(character)){ 
     outputMap2 += (character -> counter) 
     counter += 1 
    } 
    } 
} 

Respuesta

11

Más agradable solución:

inputMap.toList.filter(_._2 != 'z').map(_._2).distinct.zipWithIndex.toMap 
+1

La solución más agradable es agradable! Cumple todos los requisitos de forma compacta y comprensible. (Personalmente, haría un mapa antes de filtrar, pero ese es un detalle casi trivial.) –

5

primer lugar, si usted está haciendo esto funcionalmente, se debe utilizar un mapa de inmutable.

Entonces, para deshacerse de algo, se utiliza el filter método:

inputMap.filter(_._2 != 'z') 

y, finalmente, hacer la reasignación, sólo puede utilizar los valores (sino como un conjunto) con zipWithIndex, que se contar a partir de cero, y luego convertir de nuevo a un mapa:

inputMap.filter(_._2 != 'z').values.toSet.zipWithIndex.toMap 

Dado que el orden de los valores no va a ser preservado de todos modos *, presumiblemente, no importa que el orden puede haber sido barajadas de nuevo con la transformación establecida

Editar: Hay una solución mejor en una línea similar; ver los de Arjan. La suposición (*) es incorrecta, ya que era un LinkedHashMap. Entonces necesitas mantener el orden, que es lo que hace la solución de Arjan.

3

crearía alguna "tubería" como esta, pero esto tiene muchas operaciones y podría ser acortada. Estos dos List.map se pueden poner en uno, pero creo que tienes una idea general.

inputMap 
.toList // List((5,c), (1,a), (2,a), (3,b), (4,z)) 
.sorted // List((1,a), (2,a), (3,b), (4,z), (5,c)) 
.filterNot((x) => {x._2 == 'z'}) // List((1,a), (2,a), (3,b), (5,c)) 
.map(_._2) // List(a, a, b, c) 
.zipWithIndex // List((a,0), (a,1), (b,2), (c,3)) 
.map((x)=>{(x._2+1 -> x._1)}) // List((1,a), (2,a), (3,b), (4,c)) 
.toMap // Map((1,a), (2,a), (3,b), (4,c))

realizar estas operaciones en las listas mantiene el orden de los elementos.

2

EDIT: Interpreté mal la pregunta de OP, pensé que quería ejecutar la codificación de longitud. Esta es mi opinión sobre su pregunta real:

val values = inputMap.values.filterNot(_ == 'z').toSet.zipWithIndex.toMap 

EDIT 2: Como se ha señalado en los comentarios, utilice toSeq.distinct o similar si preservar el orden es importante.

val values = inputMap.values.filterNot(_ == 'z').toSeq.distinct.zipWithIndex.toMap 
+0

El OP no solicitó una codificación de longitud de ejecución. Vuelve a leer el problema (o el código). –

+0

Verdaderamente, léelo demasiado rápido, editado. La nueva solución se parece mucho a todas las demás como se esperaría. ¡Aclamaciones! – Janx

+0

Como Rex señaló en su propia respuesta, 'toSet' no cumple los requisitos porque no preserva la orden. –

-2

En mi experiencia, he encontrado que los mapas y los idiomas funcionales no funcionan bien. Observará que todas las respuestas hasta ahora de una forma u otra implican convertir el mapa en una lista, filtrar la lista y luego convertir la lista en un mapa.

Creo que esto se debe a que los mapas son estructuras de datos mutables por naturaleza. Tenga en cuenta que al crear una lista, que la estructura subyacente de la lista no cambia cuando agrega un nuevo elemento y si una lista verdadera, entonces un apéndice es una operación O (1) constante.Mientras que para un mapa la estructura interna de un mapa puede cambiar enormemente cuando se agrega un nuevo elemento, es decir. cuando el factor de carga se vuelve demasiado alto y el algoritmo de adición cambia el tamaño del mapa. De esta forma, un lenguaje funcional no puede simplemente crear una serie de valores y mostrarlos en un mapa a medida que avanza debido a los posibles efectos secundarios de la introducción de un nuevo par clave/valor.

Dicho esto, sigo pensando que debería haber un mejor soporte para el filtrado, el mapeo y el plegado/reducción de mapas. Como comenzamos con un mapa, sabemos el tamaño máximo del mapa y debería ser fácil crear uno nuevo.

Si desea familiarizarse con la programación funcional, le recomendaría que se desvíe de los mapas para comenzar. Quédese con las cosas para las que se diseñaron los lenguajes funcionales: manipulación de la lista.

+0

Scala proporciona mapas inmutables muy agradables. El hecho de que muchas de las soluciones se conviertan en una lista (obsérvese que Rex no lo hace) es más una consecuencia del problema que cualquier limitación que implique mapas. Al OP no parece importarle las claves originales, por lo que la estructura de datos de entrada también podría ser una lista. –

+1

Este comentario no responde a la pregunta y es objetivamente incorrecto (por ejemplo, con un árbol, excepto O (log (n)), el árbol permanece intacto al actualizarlo). Además, la pregunta del OP es solicitar la aplicación de un algoritmo que no es intrínsecamente una operación de mapeo, ya que el mapa es solo para un índice (que se puede inferir del orden de aparición). Entonces, por supuesto, muchas soluciones no usan mapas. –

+0

Uf, mi error. Solo pensé en hashmaps al mirar la pregunta. También creo que no estaba al tanto del comportamiento de un mapa enlazado (iterando por orden de inserción). Creo que para mi cordura. Me gustaría que esta pregunta sea respondida. Es la funcionalidad deseada para producir un mapa con claves/correlaciones de valores donde cada clave no es 'z' y cuyo valor es la última vez/índice en que se insertó en el mapa (excepto las inserciones de caracteres 'z'). – Dunes

9

Encuentro esta solución ligeramente más simple que arjan's:

inputMap.values.filter(_ != 'z').toSeq.distinct.zipWithIndex.toMap 

Los pasos individuales:

inputMap.values  // Iterable[Char] = MapLike(a, a, b, z, c) 
    .filter(_ != 'z') // Iterable[Char] = List(a, a, b, c) 
    .toSeq.distinct // Seq[Char]  = List(a, b, c) 
    .zipWithIndex  // Seq[(Char, Int)] = List((a,0), (b,1), (c,2)) 
    .toMap    // Map[Char, Int] = Map((a,0), (b,1), (c,2)) 

Tenga en cuenta que su problema no implica intrínsecamente un mapa como entrada, ya que estás solo descartando las llaves. Si estuviera codificar esta, probablemente me escribo una función como

def buildIndex[T](s: Seq[T]): Map[T, Int] = s.distinct.zipWithIndex.toMap 

y alegar ésta como

buildIndex(inputMap.values.filter(_ != 'z').toSeq) 
+0

Muy bueno. Aprecio particularmente los comentarios en el segundo fragmento. – Pengin

Cuestiones relacionadas