Reputation: 489
To put it straigth, I'm fairly new to Haskell and trying to solve a problem (programming exercise) I came over. Where it says I should create a function
com :: Int -> [t] -> [[t]]
that returns all possible choices of n elements, where n and list are the first and second arguments, respectively. Elements can be picked over again and in a different order. A result would be like:
com 2 [1,2,3] = [[1,1], [1,2]..[3,3]]
For the cases n = 1 and n = 2, I manage to solve the cases. The case n = 1 is quite simple, and, for the case n = 2, I would use concatenation and build it up. However, I don't understand how it can be made n-ary and work for all n. Like if suddenly a function call would be like com 10
...
Upvotes: 3
Views: 186
Reputation: 116139
Is this what you want?
> sequence (replicate 3 "abc")
["aaa","aab","aac","aba","abb","abc","aca","acb","acc"
,"baa","bab","bac","bba","bbb","bbc","bca","bcb","bcc"
,"caa","cab","cac","cba","cbb","cbc","cca","ccb","ccc"]
The above exploits the fact that sequence
, in the list monad, builds the cartesian product of a list of lists. So, we can simply replicate our list n
times, and then take the product.
(Note that "abc"
above is a shorthand for the list of charatcters ['a','b','c']
)
So, a solution could be
com n xs = sequence (replicate n xs)
or equivalently, as Daniel Wagner points out below,
com = replicateM
A final note: I do realize that this is probably not very helpful for actually learning how to program. Indeed, I pulled two "magic" functions from the library which solved the task. Still, it shows how the problem can be reduced to two subproblems: 1) replicating a value n
times and 2) building a cartesian product. The second task is a nice exercise on its own, if you don't want to use the library. You may wish to solve that starting from:
sequence :: [[a]] -> [[a]]
sequence [] = [[]]
sequence (x:xs) = ...
where ys = sequence xs
Upvotes: 5
Reputation: 77414
A more verbose way to write it, but potentially more helpful when trying to understand List
non-determinism and trying to understand exactly what the Haskell comprehension syntactic sugar really means, is to write with do
notation:
com :: Int -> [a] -> [[a]]
com 0 _ = []
com 1 xs = [[x] | x <- xs]
com n xs = do
x <- xs
let ys = com (n - 1) xs
map (x:) ys
Upvotes: 1
Reputation: 1284
First: []
is a list constructor, not a tuple. I don't know any general way to build n-ary tuple.
However, sticking to lists, if you have n = 1
case solved and n = 2
case solved try to express the latter in term of the former. Then generalize to any n
in terms of n-1
:
com n xs = concat [map (x:) (com (n-1) xs) | x <- xs ]
Upvotes: 2