2012-03-19 23 views
5

Estoy buscando una solución para mi clase Haskell.¿Dividir lista y hacer suma de sublista?

Tengo una lista de números y debo devolver SUM para cada parte de la lista. Las partes están divididas por 0. Necesito usar la función FOLDL.

Ejemplo:
lista inicial: [1,2,3,0,3,4,0,5,2,1]
sublista [[1,2,3], [3,4], [5,2,1]]
resultado [6,7,7]

tengo una función de búsqueda en la lista inicial 0:

findPos list = [index+1 | (index, e) <- zip [0..] list, e == 0] 

(retornos [4,6] para la lista inicial del ejemplo)

y función para hacer SUM con FOLDL:

sumList list = foldl (+) 0 list 

Pero fracasado por completo a poner juntos:/

---- Mi solución
Al final he encontrado algo completamente diferente que ustedes sugeridas.
Me llevó todo el día para que sea:/

groups :: [Int] -> [Int] 
groups list = [sum x | x <- makelist list] 

makelist :: [Int] -> [[Int]] 
makelist xs = reverse (foldl (\acc x -> zero x acc) [[]] xs) 

zero :: Int -> [[Int]] -> [[Int]] 
zero x acc | x == 0 = addnewtolist acc 
      | otherwise = addtolist x acc 

addtolist :: Int -> [[Int]] -> [[Int]] 
addtolist i listlist = (i : (head listlist)) : (drop 1 listlist) 

addnewtolist :: [[Int]] -> [[Int]] 
addnewtolist listlist = [] : listlist 
+0

Creo que 'result' debe ser' [6,7,8] 'en lugar de' [6,7,7] '. – Landei

Respuesta

2

Ahora que ya ha solucionado el problema por su cuenta, le muestro una versión ligeramente menos detallada. Foldr parece mejor en mi opinión para este problema *, pero debido a que solicitó foldl le mostraré mi solución con ambas funciones.

Además, su ejemplo no es válida, la suma de [5,2,1] es de 8, no 7.

La versión foldr.

makelist' l = foldr (\x (n:ns) -> if x == 0 then 0:(n:ns) else (x + n):ns) [0] l 

En esta versión, que atraviesan la lista, si el elemento actual (x) es un 0, se añade un nuevo elemento a la lista de acumulador (n: ns). De lo contrario, agregamos el valor del elemento actual al valor del elemento frontal del acumulador y reemplazamos el valor frontal del acumulador con este valor.

Paso a paso:

  1. acc = [0], x = 1. Result is [0+1]
  2. acc = [1], x = 2. Result is [1+2]
  3. acc = [3], x = 5. Result is [3+5]
  4. acc = [8], x = 0. Result is 0:[8]
  5. acc = [0,8], x = 4. Result is [0+4,8]
  6. acc = [4,8], x = 3. Result is [4+3,8]
  7. acc = [7,8], x = 0. Result is 0:[7,8]
  8. acc = [0,7,8], x = 3. Result is [0+3,7,8]
  9. acc = [3,7,8], x = 2. Result is [3+2,7,8]
  10. acc = [5,7,8], x = 1. Result is [5+1,7,8] = [6,7,8]

Ahí lo tienes!

Y la versión foldl. Funciona de manera similar a la anterior, pero produce una lista invertida, de ahí el uso de invertir al comienzo de esta función para invertir la lista.

makelist l = reverse $ foldl (\(n:ns) x -> if x == 0 then 0:(n:ns) else (x + n):ns) [0] l 

* Plegado de la lista de la derecha permite que los contras (:) función que se utilizará de forma natural, usando mi método con un pliegue izquierda produce una lista invertida. (No es probable una manera más sencilla de hacer la versión izquierdo veces que yo no había pensado en eso elimina esta trivialidad.)

+0

impresionante, gracias :) – m4recek

6

voy a dar algunos consejos, en lugar de una solución completa, ya que esto suena como puede ser una tarea.

Me gusta el desglose de los pasos que ha sugerido. Para el primer paso (pasar de una lista de números con cero marcadores a una lista de listas), sugiero hacer una recursión explícita; probar esto por una plantilla:

splits []  = {- ... -} 
splits (0:xs) = {- ... -} 
splits (x:xs) = {- ... -} 

También puede abusar groupBy si usted tiene cuidado.

Para el segundo paso, parece que ya casi está allí; el último paso que necesita es echar un vistazo a la función map :: (a -> b) -> ([a] -> [b]), que toma una función normal y la ejecuta en cada elemento de una lista.

Como un ejercicio extra, es posible que desee pensar en cómo podría hacer todo en un solo intento como un simple doblez. Es posible, e incluso no demasiado difícil, si rastreas a través de qué tipo de argumentos deberían ser foldr/foldl.

Adiciones ya que la cuestión cambió:

ya que parece que ha trabajado para encontrar una solución, ahora me siento cómodo dando algunos spoilers. =)

Sugerí dos posibles implementaciones; uno que va paso a paso, como sugirió, y otro que va de una vez.El paso a paso se podría tener este aspecto:

splits []  = [] 
splits (0:xs) = [] : splits xs 
splits (x:xs) = case splits xs of 
    []  -> [[x]] 
    (ys:yss) -> ((x:ys):yss) 

groups' = map sum . splits 

O así:

splits' = groupBy (\x y -> y /= 0) 
groups'' = map sum . splits' 

del todo a la vez la versión podría tener este aspecto:

accumulate 0 xs  = 0:xs 
accumulate n (x:xs) = (n+x):xs 

groups''' = foldr accumulate [0] 

Para compruebe que los comprende, aquí hay algunos ejercicios que le gustaría probar:

  • ¿Qué hacen splits y splits' con [1,2,3,0,4,5]? [1,2,0,3,4,0]? [0]? []? Verifica tus predicciones en ghci.
  • Predecir qué salidas de cada una de las cuatro versiones de groups (incluida la suya) para entradas como [] o [1,2,0,3,4,0], y luego probar su predicción en ghci.
  • Modifique groups''' para mostrar el comportamiento de una de las otras implementaciones.
  • Modificar groups''' para usar foldl en lugar de foldr.
+0

Y puede usar el paquete dividido de Hackage y 'splitOn [0]' si esto no es tarea. – Jedai

+0

Hola, gracias por tu ayuda :) Lamentablemente, no fue muy útil para mí. Invito que necesitaba "ejemplo para tontos". Soy realmente nuevo en Haskell, así que tuve problemas como: "cómo agregar algo a la primera lista en la lista" y "cómo escribir tipos", etc. de todos modos, ¿puede escribir la solución que sugirió? Estoy interesado en cómo debería ser :) – m4recek

+0

@ user1279158 Hecho. –

1

Como ya lo solucionó, otra versión:

subListSums list = reverse $ foldl subSum [0] list where 
    subSum xs 0 = 0 : xs 
    subSum (x:xs) n = (x+n) : xs 

(Asumiendo que usted solo tiene números no negativos en la lista)

+0

¿Dónde aparece la suposición no negativa en el código? –

+0

Si los números de un grupo sumaran hasta cero, este cero sería "sobrescrito" por el siguiente grupo, por lo que esta suma no aparecería en la lista final. – Landei

+0

¡Creo que ha entendido mal su propio código! Ninguna parte de su código verifica si la suma corriente actual es igual a '0'; además, en un nivel más empírico, 'subListSums [2, -2,0,2, -2] 'parece hacer lo correcto en ghci. –

Cuestiones relacionadas