2010-08-02 13 views
9

Dadas dos listas, puedo producir una lista de todas las permutaciones el producto cartesiano de estas dos listas:Calcular n-arias producto cartesiano

permute :: [a] -> [a] -> [[a]] 
permute xs ys = [ [x, y] | x <- xs, y <- ys ] 

Example> permute [1,2] [3,4] == [ [1,3], [1,4], [2,3], [2,4] ] 

¿Cómo se amplía permute por lo que en lugar de tomar dos listas , se necesita una lista (longitud n) de las listas y devuelve una lista de listas (longitud n)

permute :: [[a]] -> [[a]] 

Example> permute [ [1,2], [3,4], [5,6] ] 
      == [ [1,3,5], [1,3,6], [1,4,5], [1,4,6] ] --etc 

no pude encontrar nada relevante sobre Hoogle .. la única función que concuerden con la firma era transpose, que doesn no produce la salida deseada

Editar: Creo que la versión de 2 listas de esto es esencialmente el Cartesian Product, pero no puedo entender la implementación del n-ary Cartesian Product. ¿Alguna sugerencia?

Respuesta

22
Prelude> sequence [[1,2],[3,4],[5,6]] 
[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]] 
+2

Mientras secuencia resuelve el pro blem, estaba realmente interesado en cómo funcionaría esto. La [implementación] (http://haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0.0/src/Control-Monad.html#sequence) usa mónadas; ¿Hay alguna manera de calcular el producto sin usar mónadas? (por ejemplo, en un idioma que no incluye mónadas) – guhou

+0

@ BleuM937: para la lista de mónadas, 'secuencia' significa" para cada elemento en la primera lista, anteponerla a cada lista obtenida secuenciando las listas restantes ". Básicamente es la forma más obvia de escribir un producto cartesiano utilizando un doblez correcto. –

3

Como complemento a la respuesta de jleedev (no podía dar formato a esto en los comentarios):

Una sustitución sin control rápido de las funciones de lista de los monádicos:

sequence ms = foldr k (return []) ms 
    where 
    k m m' = do { x <- m; xs <- m'; return (x:xs) } 

....

k m m' = m >>= \x -> m' >>= \xs -> [x:xs] 
    k m m' = flip concatMap m $ \x -> flip concatMap m' $ \xs -> [x:xs] 
    k m m' = concatMap (\x -> concatMap (\xs -> [x:xs]) m') m 

....

sequence ms = foldr k ([[]]) ms 
    where 
    k m m' = concatMap (\x -> concatMap (\xs -> [x:xs]) m') m 
+3

Esto se puede simplificar aún más eliminando una concatenación superflua en 'k m m '= concatMap (\ x -> map (x :) m') m'. También podría escribirlo como una lista de comprensión como '[x: xs | x <- m, xs <- m '] '. –

4

Encontré el artículo de Eric Lippert en computing Cartesian product with LINQ bastante útil para mejorar mi comprensión de lo que estaba pasando. He aquí una traducción directa, más o menos:

cartesianProduct :: [[a]] -> [[a]] 
cartesianProduct sequences = foldr aggregator [[]] sequences 
        where aggregator sequence accumulator = 
         [ item:accseq |item <- sequence, accseq <- accumulator ] 

O con más, los nombres de parámetros sin sentido concisas "Haskell-Y";)

cartesianProduct = foldr f [[]] 
        where f l a = [ x:xs | x <- l, xs <- a ] 

Esto termina siendo bastante similar al SCLV publicado después de todo .

+0

Es lo mismo que sclv, de hecho, hasta algunas diferencias en la sintaxis. Además, usted sabe esto (después de haber escrito la traducción), pero para cualquier otra persona: observe que el ejemplo de Eric Lippert usa un doblez * izquierda * en lugar de un doblez derecho, pero esto no hace diferencia porque la función es estricta en los lomos de las listas de todos modos (como con 'sequence' en general). –

2

Si desea tener más control sobre la salida, se puede utilizar una lista como funtor aplicativo, por ejemplo:

(\x y z -> [x,y,­z]) <$> [1,2]­ <*> [4,5]­ <*> [6,7] 

Digamos que usted desea una lista de tuplas en su lugar:

(\x y z -> (x,y,­z)) <$> [1,2]­ <*> [4,5]­ <*> [6,7] 

Y se ve algo genial, también ...

2

Aquí está mi manera de implementarlo simplemente, usando solo listas de comprensión.

crossProduct :: [[a]] -> [[a]] 
crossProduct (axis:[]) = [ [v] | v <- axis ] 
crossProduct (axis:rest) = [ v:r | v <- axis, r <- crossProduct rest ] 
1

Puede hacerlo de 2 maneras:

  1. Utilizando lista por comprensión

cp :: [[a]] -> [[a]]

cp [] = [[]]

cp (xs: xss) = [x: ys | x < - xs, ys < - XSS cp]

  1. Uso de un pliegue

CP1 :: [[a]] -> [[a ]]

f [[]] xs

CP1 xs = foldr

where f xs xss = [x:ys | x <- xs, ys <- xss] 
Cuestiones relacionadas