Hay varias maneras de resolver esto y Adam ya te ha dado una manera eficiente basada en mapas. Sin embargo, creo que una solución que use solo listas y recursiones también sería instructiva, especialmente al aprender Haskell. Como ya has recibido una respuesta, espero poder escribir una solución aquí sin estropear nada.
La forma en que me acerqué a esto es pensar en cómo podemos reducir la lista de entrada a la lista de salida. Comenzamos con
El objetivo es terminar con una lista de resultados, donde cada tupla comienza con un carácter único. La creación de dicha lista de resultados se puede realizar de forma incremental: comience con una lista vacía y luego inserte tuplas en la lista, asegurándose de no duplicar los caracteres.El tipo sería
insertInResult :: (Char, Integer) -> [(Char, Integer)] -> [(Char, Integer)]
Se necesita el par, como ('A',3)
y lo inserta en una lista existente de pares únicos. El resultado es una nueva lista de pares únicos. Esto se puede hacer de esta manera:
insertInResult (c, n) [] = [(c, n)]
insertInResult (c, n) ((c', n'):results)
| c == c' = (c, n + n') : results
| otherwise = (c', n') : (insertInResult (c, n) results)
Explicación: insertar una tupla en una lista de resultados vacío es fácil, basta con insertar la misma. Si la lista de resultados no está vacía, obtenemos el primer resultado (c', n')
con coincidencia de patrones. Verificamos si los personajes coinciden con el guardia, y agregamos los números si es así. De lo contrario, simplemente copiemos la tupla del resultado e inserte la tupla (c, n)
en los resultados restantes.
Ahora podemos hacer
*Main> insertInResult ('A',3) []
[('A',3)]
*Main> insertInResult ('B',2) [('A',3)]
[('A',3),('B',2)]
El siguiente paso es utilizar insertInResult
repetidamente en la lista de entrada de manera que construimos una lista de resultados. Llamé a esta función sumPairs'
ya llamé a la función de nivel superior sumPairs
:
sumPairs' :: [(Char, Integer)] -> [(Char, Integer)] -> [(Char, Integer)]
sumPairs' [] results = results
sumPairs' (p:pairs) results = sumPairs' pairs (insertInResult p results)
Es una función simple que sólo repite en el primer argumento e inserta cada par en la lista de resultados. El último paso es llamar a esta función auxiliar con una lista de resultados vacía:
sumPairs :: [(Char, Integer)] -> [(Char, Integer)]
sumPairs pairs = sumPairs' pairs []
¡Funciona! :-)
*Main> sumPairs [('A',3), ('B',2), ('C',2), ('A',5), ('C',3), ('C',2)]
[('A',8),('B',2),('C',7)]
La complejidad de esta solución no es tan bueno como el basado en Data.Map
. Para una lista con n pares, llamamos insertInResult
n veces desde sumPairs'
. Cada llamada al insertInResult
puede iterar hasta n veces hasta que encuentre una tupla de resultado coincidente, o llegue al final de los resultados. Esto da una complejidad de tiempo de O (n ²). La solución basada en Data.Map
tendrá O (n registro n) complejidad del tiempo ya que utiliza log n tiempo para insertar y actualizar cada uno de los n elementos.
Tenga en cuenta que esta es la misma complejidad que habría obtenido si hubiera ordenado la lista de entrada y luego escaneada una vez para sumar tuplas adyacentes con el mismo carácter:
sumPairs pairs = sumSorted (sort pairs) []
sumSorted [] result = result
sumSorted (p:pairs) [] = sumSorted pairs [p]
sumSorted ((c,n) : pairs) ((c',n') : results)
| c == c' = sumSorted pairs ((c,n + n') : results)
| otherwise = sumSorted pairs ((c,n) : (c',n') : results)
mucho más fácil de lo que pensaba ¡ser! –
Bueno ... Espero no haber aprovechado ninguna oportunidad de aprendizaje en las versiones anteriores de mi respuesta. –