构造两个列表的笛卡尔积,每对添加一个随机数

Jiv*_*van 2 random haskell list

我正在尝试获得以下输出:

[(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)]
Run Code Online (Sandbox Code Playgroud)

这是笛卡尔积,[1..3], [5..7]其中每个结果元组都获得一个随机数作为其第三个元素。

我试过下面的代码

[(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)]
Run Code Online (Sandbox Code Playgroud)

问题是这会生成一个随机数并一直重复(假设这次是 42):

[(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)]
Run Code Online (Sandbox Code Playgroud)

然后我想,当然,让我们实际生成length [1..3] * length [5..7]数字:

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
Run Code Online (Sandbox Code Playgroud)

但现在这将随机数列表组合到产品中:

[(1,5,66),(1,5,41) ... (1,6,66),(1,6,41) ... (1,7,66),(1,7,41)]
Run Code Online (Sandbox Code Playgroud)

是否有可能以直接方式获得所需的输出,从而避免最初构建二元组列表,然后使用生成的相同长度的随机数列表对其进行排序randomRs

jpm*_*ier 5

问题是笛卡尔积与 zip 操作完全不同。笛卡尔积的长度是其操作数长度的乘积,而 zip 结果的长度是其操作数长度的最小值。

我们仍然可以分两步操作:

$ 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)]
 ?> 

Run Code Online (Sandbox Code Playgroud)

所以我们差不多完成了。

 ?> 
 ?> 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)]
 ?> 
Run Code Online (Sandbox Code Playgroud)

或者:

 ?> 
 ?> 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)]
 ?> 
Run Code Online (Sandbox Code Playgroud)