2011-12-13 16 views
8

tengo la siguiente lista (que es una lista de longitud 2, pero en mi asignación que tienen una longitud + n lista)“reemplazar” a 3 tupla

xxs = [(11,22,[(33,33,33),(44,44,44)]),(55,66,[(77,77,77),(88,88,88)])] 

Estoy tratando de “reemplazar” uno 3-tupla (p1 o p2 o p3 o p4 de la imagen de abajo) por índice de lista (n) y por índice de sub-lista (p).

Visual of list breakdown

La función, al final, debería ser como:

fooo newtuple n p = (…) 

Por ejemplo: (sustituir p3 para (98,98,98):

fooo (98,98,98) 2 1 
[(11, 22, [(33,33,33) , (44,44,44)]) , (55, 66, [(98,98,98),(88,88,88)])] 

I planeó el código como sigue estos pasos:

  1. Acceda al pn que quiero cambiar. Me las arreglo para lograrlo por:

    fob n p = ((aux2 xxs)!!n)!!p 
        where aux2 [] = [] 
         aux2 ((_,_,c):xs) = c:aux2 xs 
    
  2. “reemplazar” la 3-tupla. Realmente necesito un poco de ayuda aquí. Estoy atascado. el mejor código (en mi cabeza tiene algún sentido) que he hecho: (recuerde: por favor, no seas tan malo en mi código, sólo he estado estudiando Haskell sólo por 5 semanas)

    foo n p newtuple = fooAux newtuple fob 
        where fooAux _ [] = [] 
          fooAux m ((_):ds) = m:ds 
          fob n p = ((aux2 xxs)!!n)!!p 
           where aux2 [] = [] 
            aux2 ((_,_,c):xs) = c:aux2 xs 
    
  3. Finalmente volveré a armar todo, usando splitAt.

¿Mi enfoque del problema es correcto? Realmente apreciaría algo de ayuda en el paso 2.

Respuesta

7

Soy un poco nuevo en Haskell también, pero veamos si no podemos encontrar una forma decente de hacer esto.

Por lo tanto, fundamentalmente lo que estamos tratando de hacer es modificar algo en una lista. Usando programación funcional me gustaría mantenerlo un poco general, así que hagamos una función update.

update :: Int -> (a -> a) -> [a] -> [a] 
update n f xs = pre ++ (f val) : post 
    where (pre, val:post) = splitAt n xs 

que ahora tendrá un índice, una función y una lista y cambie el nth en la lista con el resultado de la función que se le aplica.

En nuestro problema más grande, sin embargo, tenemos que actualizar en un contexto anidado. Afortunadamente, nuestra función update toma una función como argumento, ¡así que podemos llamar al update dentro de esa también!

type Triple a = (a,a,a) 
type Item = (Int, Int, [Triple Int]) 

fooo :: Triple Int -> Int -> Int -> [Item] -> [Item] 
fooo new n p = update (n-1) upFn 
    where upFn (x,y,ps) = (x,y, update (p-1) objFn ps) 
     objFn _ = new 

Todo fooo tiene que hacer es actualizar la llamada dos veces (una dentro de la otra llamada) y hacer un poco de "limpieza" de trabajo (poniendo el resultado en la tupla correctamente). El (n-1) y (p-1) se debían a que parece que está indexando comenzando en 1, mientras que Haskell comienza en 0.

deja apenas para ver si funciona con nuestro caso de prueba:

*Main> fooo (98,98,98) 2 1 [(11,22,[(33,33,33),(44,44,44)]),(55,66,[(77,77,77),(88,88,88)])] 
[(11,22,[(33,33,33),(44,44,44)]),(55,66,[(98,98,98),(88,88,88)])] 
+0

En primer lugar, su explicación fue muy notable.Segundo: todavía tengo mucho que aprender, pero aprendí mucho con su respuesta. Tercero: me paso la mitad del día tratando de resolver esto, y lo resuelves en unos minutos (realmente tengo mucho que aprender y estudiar). Finalmente, ¡un GRAN agradecimiento! _o_ – Nomics

4

por lo que ha intentado utilizar alguna función ya hecho, (!!). Podría acceder a un elemento en una lista, pero olvidó su lugar allí, por lo que no pudo actualizar. Tiene una solución ofrecida, usando otra función lista para usar split, que desgarra una lista en dos partes, y (++) que las pega de nuevo en una sola.

Pero para tener una idea real de ella, lo que sospecha que su misión tenía como objetivo, en primer lugar (es fácil de olvidar un nombre de función, y es igual de fácil de escribir usted mismo uno nuevo en su lugar), se podría intentar escribir el primero, (!!), usted mismo. Entonces verás que es muy fácil modificarlo para que también pueda actualizar la lista.

para escribir su función, mejor piensan que es una ecuación de equivalencia:

myAt 1 (x:xs) = x 
myAt n (x:xs) | n > 1 = ... 

cuando n es cero, sólo quita el elemento de cabeza. ¿Qué hacemos cuando no es así? Intentamos acercarnos al cero. Puede completar los espacios en blanco.

Así que aquí hemos devuelto el elemento encontrado. ¿Qué pasa si queremos reemplazarlo? Reemplazarlo con qué? - Esto llama a otro parámetro a la existencia,

myRepl 1 (x:xs) y = (y:xs) 
myRepl n (x:xs) y | n > 1 = x : myRepl ... 

Ahora puede completar el resto, creo.

Por último, Haskell es un lenguaje vago. Eso significa que solo llama a la existencia los elementos de una lista que son necesarios, eventualmente. ¿Qué sucede si reemplaza el elemento 7º, pero solo se solicitan primero los 3 primeros? El código que usa split realmente demandará los 7 elementos, por lo que puede devolver los primeros 3 cuando más tarde se los solicite.

En su caso, se desea reemplazar de forma anidada, y el valor para reemplazar a la antigua con depende del valor antiguo: newVal = let (a,b,ls)=oldVal in (a,b,myRepl p ls newtuple). Así que de hecho lo necesario para volver a escribir usando funciones en lugar de valores (de modo que cuando se utilizó y antes, const y iría):

myUpd 1 (x:xs) f = (f x:xs) 
myUpd n ... = ... 

y toda su llamada se convierte en myUpd n xxs (\(a,b,c)->(a,b,myUpd ... (const ...))).

+0

Es una solución más cercana a la que he probado. Trataré de completar el (...). La explicación está todo allí. :) ¡¡Gracias!! – Nomics

+1

@Nomics lo que quise decir es que, para escribir su función, piense en ella como si ya la hubiera escrito, como si ya la tuviera disponible para su uso, y simplemente escriba algunas ecuaciones de equivalencia basadas en las cualidades que se supone que debe tener, leyes que se supone que debe seguir. Estas ecuaciones se convertirán en la definición de la función en sí mismas - siempre y cuando siga las leyes de la misma, mantenga las invariantes antes y después de su uso, por ejemplo: _la lista con 1st elt reemplazado es solo eso; la lista con _n_th elt reemplazado es el primer elt prefijado al resto con _ (n-1) _-ésimo reemplazado. Eso es todo. –

4

En primer lugar, necesitamos una función general para trazar un cierto elemento de una lista, por ejemplo .:

mapN :: (a -> a) -> Int -> [a] -> [a] 
mapN f index list = zipWith replace list [1..] where 
    replace x i | i == index = f x 
       | otherwise = x 

Podemos utilizar esta función dos veces, por la lista externa y las listas internas. Hay una complicación poco como la lista interna es parte de una tupla, por lo que necesitamos otra función auxiliar:

mapTuple3 :: (c -> c) -> (a,b,c) -> (a,b,c) 
mapTuple3 f (x,y,z) = (x,y,f z) 

Ahora tenemos todo lo que necesitamos para aplicar la función de reemplazar a nuestro caso de uso:

fooo :: Int -> Int -> (Int,Int,Int) -> [(Int,Int,[(Int,Int,Int)])] 
fooo n p newTuple = mapN (mapTuple3 (mapN (const newTuple) p)) n xxs 

Por supuesto, en la lista interna, no necesitamos considerar el valor anterior, por lo que podemos usar const :: a -> (b -> a) para ignorar ese argumento.