Вычислить N-арное декартово произведение
учитывая два списка, я могу произвести список всех перестановок декартово произведение этих двух списков:
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] ]
как расширить перестановку, чтобы вместо того, чтобы принимать два списка, он принимает список (длина 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
, который не производит желаемого результата.
Edit: я думаю, что 2-list версия этого по существу является Декартова Произведения, но я не могу обернуть мою голову вокруг реализации N-арное декартово произведение. Любой указатели?
6 ответов
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]]
Я нашел статью Эрика Липперта на вычисление Декартового произведения с помощью 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.
в качестве дополнения к ответу jleedev (не удалось отформатировать это в комментариях):
быстрая непроверенная подстановка функций списка для монадических:
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
Если вы хотите иметь больше контроля над выходом, вы можете использовать список в качестве аппликативного функтора, например:
(\x y z -> [x,y,z]) <$> [1,2] <*> [4,5] <*> [6,7]
предположим, вам нужен список кортежей:
(\x y z -> (x,y,z)) <$> [1,2] <*> [4,5] <*> [6,7]
и это тоже выглядит круто...
вот мой способ его реализации просто, используя только список.
crossProduct :: [[a]] -> [[a]]
crossProduct (axis:[]) = [ [v] | v <- axis ]
crossProduct (axis:rest) = [ v:r | v <- axis, r <- crossProduct rest ]
вы можете сделать это 2 способами:
- использование списка понимания
cp :: [[a]] - > [[a]]
cp [] = [[]]
СР (хз:межсайтовый скриптинг) = [ х:ю | х
- используя складки
cp1 :: [[a]] - > [[a]]
cp1 xs = foldr f [[]] xs
where f xs xss = [x:ys | x <- xs, ys <- xss]