Construct a cartesian product of two lists with a random number added to each pair

Viewed 57

I'm trying to get to the following output:

[(1,5,54), (1,6,34), (1,7,16), (2,5,59), (2,6,29), (2,7,71), (3,5,44), (3,6,67), (3,7,24)]

which is the cartesian product of [1..3], [5..7] where each resulting tuple gets a random number as its third element.

I've tried the following code

hello :: IO [(Int, Int, Int)]
hello = do
    g <- getStdGen
    let zyx = do
            x <- [1..3]
            y <- [5..7]
            z <- take 1 $ randomRs (1, 100) g
            pure (x, y, z)
    pure zyx

The problem is that this generates one single random number and repeats it all along (let's say it was 42 this time):

[(1,5,42), (1,6,42), (1,7,42), (2,5,42), (2,6,42), (2,7,42), (3,5,42), (3,6,42), (3,7,42)]

Then I thought, sure, let's actually generate length [1..3] * length [5..7] numbers instead:

-- ...
z <- take (length [1..3] * length [5..7]) randomRs (1, 100) g
-- ...

but now this combines the list of random numbers into the product:

[(1,5,66),(1,5,41) ... (1,6,66),(1,6,41) ... (1,7,66),(1,7,41)]

Is it possible to get to the desired output in a direct manner, that avoids constructing the list of two-tuples initially and then sort-of zipping it with a same-length list of random numbers generated with randomRs?

1 Answers

The problem is that a Cartesian product is completely different from a zip operation. The length of a Cartesian product is the product of its operand lengths, while the length of a zip result is the minimum of its operand lengths.

We can still operate in two steps:

$ ghci
 λ> 
 λ> import System.Random
 λ> import Control.Applicative
 λ> 
 λ> liftA2 (,) [1..3] [5..7]
 [(1,5),(1,6),(1,7),(2,5),(2,6),(2,7),(3,5),(3,6),(3,7)]
 λ> 
 λ> g0 = mkStdGen 4243
 λ> rds = randomRs (1,100) g0
 λ> 
 λ> zip (liftA2 (,) [1..3] [5..7]) rds
 [((1,5),24),((1,6),90),((1,7),79),((2,5),39),((2,6),96),((2,7),27),((3,5),96),((3,6),53),((3,7),77)]
 λ> 

So we are almost done.

 λ> 
 λ> map (\((a,b),c) -> (a,b,c)) $ zip (liftA2 (,) [1..3] [5..7]) rds
[(1,5,24),(1,6,90),(1,7,79),(2,5,39),(2,6,96),(2,7,27),(3,5,96),(3,6,53),(3,7,77)]
 λ> 

Alternatively:

 λ> 
 λ> zipWith (\(a,b) c -> (a,b,c))  (liftA2 (,) [1..3] [5..7])  rds
[(1,5,24),(1,6,90),(1,7,79),(2,5,39),(2,6,96),(2,7,27),(3,5,96),(3,6,53),(3,7,77)]
 λ> 
Related