简体   繁体   中英

Haskell create an n-ary tuple from given input

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

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

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 ]

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM