0

I have an interesting problem to figure out involving nested list comprehensions. I will redact unnecessary details for this post, as such the problem I'm trying to solve may seem a little peculiar.

I have two tuples as follows ("X", "Y", "Z") and ("1", "2"). The contents of these will only ever be as written above.

Using these two lists, I need to construct a list of 3-tuples, with every combination of the contents of the above two lists. An example of one of these tuples would be ("X2", "Z2", "Y1). Note the numbers always come after the letters.

I know I need to use a list comprehension for this, but it seems like I need to have a nested list comprehension somehow, which I am not familiar with.

I started with this:

[(comb1, comb2, comb2) | {- What goes here? -}]

I am not sure how to proceed. How can I solve this problem with a nested list comprehension? I believe a nested list comprehension is the best way to solve this problem but if you know of a better way please explain that instead.

EDIT with some further details:

This list of tuples will be output by a function that would look something like this:

init :: [(String, String, String)]
init = [(comb1, comb2, comb3) | {- What goes here? -}]

The return value from the init function will be used in the context of a larger program that will need to perform tasks on this list of 3-tuples. The actual two tuples, ("X", "Y", "Z") and ("1", "2"), will be hardcoded into the list comprehension of the init function.

There are more combinations than just what a cartesian product gives. The cartesian product would result in [["X1","Y1","Z1"],["X2","Y2","Z2"]]. However, the letters need not be in order, and some letters might not appear in one of the results at all. ["Z1", "X2", "X1"] is also a valid combination

JavascriptLoser
  • 1,853
  • 5
  • 34
  • 61
  • 1
    What should the actual output of your function be, as well as its type? If you know your input is always the same input, why even have input at all? It seems to me this can be reduced to taking all the permutations with replacement of a certain length of a list (e.g. `["1", "2"]`), then just zipping each of those with the other input. – user2407038 Aug 27 '17 at 03:09
  • @user2407038 I've added some extra information to the post. The "input" won't really be input but rather hardcoded into the `init` function. – JavascriptLoser Aug 27 '17 at 03:18
  • Explain the downvotes please? – JavascriptLoser Aug 27 '17 at 04:41

1 Answers1

1

Is it always a 3-tuple and a 2-tuple? If so, you might as well just do a direct computation:

getInit (a,b,c) (x,y) =
  [ (a++x,b++x,c++y)
  , (a++y,b++y,c++y)
  ]

But if you can use lists instead, there are several ways of performing a Cartesian product:

[[x++y | x <- ["A","B","C"]] | y <- ["1","2"]]

Or, if you prefer monads to list comprehensions:

import Control.Monad

combos = liftM2 (++)
init = combos ["A","B","C"] ["1","2"]
Andrew Lei
  • 335
  • 2
  • 9
  • This is helpful but not exactly correct. There are more combinations than just what the cartesian product gives. The cartesian product would result in `[["A1","B1","C1"],["A2","B2","C2"]]`. However, the letters need not be in order, and some letters might not appear in one of the results at all. `["C1", "B2", "B1"]` is also a valid combination. – JavascriptLoser Aug 27 '17 at 06:36
  • @JavascriptLoser Just [pick 3](https://stackoverflow.com/a/30285491/8387889) from the list of six – Andrew Lei Aug 27 '17 at 06:56
  • I'm not sure I understand what you mean, but using code from the question you linked, `arrange 3 ["A","B","C","1","2"]` gives me ALMOST what I need. An example of a list generated by that function is `["1", "B", "A"]`. I need 3 pairs, where a pair is one letter and one number. I'm not sure how to combine the code you've written and the code from that question. – JavascriptLoser Aug 27 '17 at 07:13
  • `import Control.Monad; pick :: Int -> [a] -> [[a]]; pick 0 _ = [[]]; pick _ [] = []; pick n (x:xs) = map (x:) (pick (n-1) xs) ++ pick n xs; combos = liftM2 (++); init = pick 3 $ combos ["A","B","C"] ["1","2"];` – Andrew Lei Aug 27 '17 at 07:17
  • Hm, don't know how to do line breaks here. But the important part is `init = pick 3 $ combos ["A","B","C"] ["1","2"]`. – Andrew Lei Aug 27 '17 at 07:18
  • My compiler is giving me a type error for the `combos` function. What is the type declaration? – JavascriptLoser Aug 27 '17 at 07:21
  • `combos :: [String] -> [String] -> [[String]]` – Andrew Lei Aug 27 '17 at 07:22
  • This gives me ` * Couldn't match type Char with [Char] Expected type: [String] -> [String] -> [[String]] Actual type: [[String]] -> [[String]] -> [[String]] * In the expression: liftM2 (++) In an equation for combos': combos = liftM2 (++)` – JavascriptLoser Aug 27 '17 at 07:27
  • Oops, that was wrong. It's actually `combos :: Monad m => m [a] -> m [a] -> m [a]` – Andrew Lei Aug 27 '17 at 07:29
  • Oh, although specifically in this instance `combos :: [String] -> [String] -> String]` should be what actually works. Your input should be something like ["A","B","C"]. – Andrew Lei Aug 27 '17 at 07:32