Given a list of lists of length x
where all the sublists have the same length y
, output the y^x
lists of length x
that contain one item from each sublist.
Example (x = 3
, y = 2
):
[ [1, 2], [3, 4], [5, 6] ]
Output (2^3 == 8
different outputs):
[ [1, 3, 5], [1, 4, 5], [1, 3, 6], [1, 4, 6],
[2, 3, 5], [2, 4, 5], [2, 3, 6], [2, 4, 6] ]
Ruby
I wrote actual code to perform this task, but in Ruby, as it is the language I am most comfortable with.
def all_combinations(lst)
lst.inject {|acc, new| acc.product(new).map(&:flatten) }
end
Type
Input is a list of lists that contains items of type a and so is the output.
allProduct :: [[a]] -> [[a]]
Cartesian product, flattening and folding
Looking at my Ruby solution makes me think that a good use of these functions may be enough to solve the problem. The problem is that while the Cartesian Product outputs a list of tuples but I need a list of lists.
Note: This post is written in literate Haskell. Save it as *.lhs and load it in GHCi.
> -- remove this line if you don't have QuickCheck installed
> import Test.QuickCheck
Lets start with a simple variant of allProduct
:
> allProductFirst :: [[a]] -> [[a]]
> allProductFirst [] = [[]]
> allProductFirst (x:xs) =
Now x
itself is a list again. Let's say that allProduct xs
would give us the product of the other lists.
> let rest = allProductFirst xs
What do we need to do? We need to create a new list for every element in x
and concat them together:
> in concatMap (\k -> map (k:) rest) x
Note that this variant isn't 100% correct, as allProduct []
is [[]]
.
How would this look like if we were to use the Monad
instance of []
?
do
notation> allProduct' :: [[a]] -> [[a]]
> allProduct' [] = [[]]
> allProduct' (x:xs) = do
We want to take every element of x
> elementOfX <- x
and cons it onto all the possible suffixes our list can have:
> rest <- allProduct' xs
> return $ elementOfX : rest
This means we're basically evaluation every action inside our list monad. But there's a function for that: sequence :: Monad m => [m a] -> m [a]
. If we use m ~ []
, its type can be specialized to sequence :: [[a]] -> [[a]]
.
sequence
We end up with our last variant:
> allProduct :: [[a]] -> [[a]]
> allProduct = sequence
We use QuickCheck to test that it's likely the same as allProductFirst
and allProduct'
:
> main :: IO ()
> main = do
> quickCheck $
> forAll (choose (1,8)) $ \n -> -- number of lists
> forAll (choose (1,4)) $ \s -> -- number of elements per list
> forAll (vectorOf n $ vector s) $ \xs ->
> allProduct' xs === allProduct (xs :: [[Integer]]) .&.
> allProductFirst xs === allProduct xs
Use :main
in GHCi, runhaskell
or compile and run your program, and you
should end up with 100 passed tests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With