Рассчитать n-арное декартово произведение - PullRequest
11 голосов
/ 02 августа 2010

Учитывая два списка, я могу создать список всех перестановок Декартово произведение этих двух списков:

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] ]

Как расширить permute, чтобы вместо двух списков он брал список (длина n) списков и возвращал список списков (длина 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

Я не смог найти ничего релевантного в Hoogle ... единственная функция, соответствующая подписи, была transpose, которая не выдает желаемый результат.

Редактировать: Я думаю, что версия с 2 списками по сути является декартовым продуктом , но я не могу обернуться вокруг реализации n-арного декартового продукта . Есть указатели?

Ответы [ 6 ]

23 голосов
/ 02 августа 2010
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]]
4 голосов
/ 02 августа 2010

Мне показалось, что статья Эрика Липперта о вычислении декартового произведения с помощью LINQ весьма полезна для улучшения моего понимания происходящего.Вот более-менее прямой перевод:

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

Или с более краткими, бессмысленными именами параметров "Haskell-y";)

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

В конечном итоге это очень похоже наsclv опубликован в конце концов.

3 голосов
/ 02 августа 2010

В качестве дополнения к ответу Джлидева (не удалось отформатировать это в комментариях):

Быстрая неконтролируемая замена списочных функций на монадические:

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
2 голосов
/ 12 апреля 2016

Вот мой способ реализовать это просто, используя только списки.

crossProduct :: [[a]] -> [[a]]
crossProduct (axis:[]) = [ [v] | v <- axis ]
crossProduct (axis:rest) = [ v:r | v <- axis, r <- crossProduct rest ]
2 голосов
/ 03 августа 2010

Если вы хотите лучше контролировать вывод, вы можете использовать список в качестве аппликативного функтора, например ::10000

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

Допустим, вам нужен список кортежей:

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

И это тоже выглядит круто ...

1 голос
/ 13 ноября 2017

Вы можете сделать это двумя способами:

  1. Используя понимание списка

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

cp [] = [[]]

cp (xs: xss) = [x: ys |x <- xs, ys <- cp xss] </p>

Использование сгиба

cp1 :: [[a]] -> [[a]]

cp1 xs = foldr f [[]] xs

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