如果我正确理解了这个问题,那么应该是one每个(Int,Int)棋盘位置的随机值。所以这个问题不能通过在理解列表中添加第三个子句来解决,例如:
field n = [(l,r,v) | l <- [0..n], r <- [0..n], v <- someRandomStuff]
作为长度field
表达式将是 (n+1)x(n+1)x(随机内容的长度),而你想要的只是 (n+1)x(n+1)。
一种可能性包括分两步操作:
- 生成所需的 (n+1)*(n+1) 个 0 到 3 之间的随机值
- 将其与 (l,r) 值相结合
我假设读者了解命令式语言的伪随机数生成。
给定一个种子,您可以使用函数 mkStdGen 返回的一次性随机数生成器来生成随机值,使用函数randomRs https://hackage.haskell.org/package/random-1.1/docs/System-Random.html。让我们使用一个ghci
会话作为测试平台。
关于步骤1:
λ> import System.Random
λ> :t randomRs
randomRs :: (Random a, RandomGen g) => (a, a) -> g -> [a]
λ>
λ> seed1=42
λ>
λ> getVSeq n seed = let rng0 = mkStdGen seed in take ((n+1)^2) (randomRs (0,3) rng0)
λ>
λ> getVSeq 5 seed1
[1,1,3,0,2,1,0,1,0,1,3,1,2,0,2,3,1,1,3,2,0,2,2,0,2,0,0,0,1,0,2,1,0,2,0,1]
λ>
λ> length $ getVSeq 5 seed1
36
λ> field0 n = [(l,r) | l <- [0..n], r <- [0..n]]
λ> field0 5
[(0,0),(0,1),(0,2),(0,3),(0,4),(0,5),(1,0),(1,1),(1,2),(1,3),(1,4),(1,5),(2,0),(2,1),(2,2),(2,3),(2,4),(2,5),(3,0),(3,1),(3,2),(3,3),(3,4),(3,5),(4,0),(4,1),(4,2),(4,3),(4,4),(4,5),(5,0),(5,1),(5,2),(5,3),(5,4),(5,5)]
λ>
λ>
λ>
λ> length $ field0 5
36
λ>
现在关于步骤 2,函数zip
almost解决了我们的问题,只是我们没有完全得到三元组:
λ>
λ> sol0 n seed = zip (field0 n) (getVSeq n seed)
λ> sol0 5 seed1
[((0,0),1),((0,1),1),((0,2),3),((0,3),0),((0,4),2),((0,5),1),((1,0),0),((1,1),1),((1,2),0),((1,3),1),((1,4),3),((1,5),1),((2,0),2),((2,1),0),((2,2),2),((2,3),3),((2,4),1),((2,5),1),((3,0),3),((3,1),2),((3,2),0),((3,3),2),((3,4),2),((3,5),0),((4,0),2),((4,1),0),((4,2),0),((4,3),0),((4,4),1),((4,5),0),((5,0),2),((5,1),1),((5,2),0),((5,3),2),((5,4),0),((5,5),1)]
λ>
所以我们需要按摩结果sol0
一点点:
λ>
λ> sol1 n seed = let flatten = (\((a,b),c) -> (a,b,c)) in map flatten (sol0 n seed)
λ> sol1 5 seed1
[(0,0,1),(0,1,1),(0,2,3),(0,3,0),(0,4,2),(0,5,1),(1,0,0),(1,1,1),(1,2,0),(1,3,1),(1,4,3),(1,5,1),(2,0,2),(2,1,0),(2,2,2),(2,3,3),(2,4,1),(2,5,1),(3,0,3),(3,1,2),(3,2,0),(3,3,2),(3,4,2),(3,5,0),(4,0,2),(4,1,0),(4,2,0),(4,3,0),(4,4,1),(4,5,0),(5,0,2),(5,1,1),(5,2,0),(5,3,2),(5,4,0),(5,5,1)]
λ>
这就是我理解你想要的。
如果这是您的应用程序中唯一使用随机数,那么这可能就足够了。否则,恐怕需要在 Haskell 函数式编程环境中建立一些有关随机数生成的知识。你可能想开始here https://www.schoolofhaskell.com/school/starting-with-haskell/libraries-and-frameworks/randoms or there https://en.wikibooks.org/wiki/Haskell/Libraries/Random.
此外,正如 Thomas M. DuBuisson 所提到的,这已在几个 SO 问题中得到解决。您可以使用本地搜索引擎。这是最近的其中之一 https://stackoverflow.com/questions/57836652/how-can-i-generate-different-random-values-in-haskell例如。
如果您需要收回发电机以供重复使用怎么办?
在这种情况下,您需要有一个函数,它接受预先构建的生成器,并以 (list,finalRng) 对的形式返回“字段”三元组列表和生成器(的最终状态)。
您可以将困难(随机)工作分包给一个函数,该函数返回另一个更简单的对,其中仅包含 v 值列表和生成器的最终状态。该函数可以用递归方式编写,如下所示。
import System.Random
import Control.Monad.Random
-- returns the random v values AND the final state of the generator
seqAndGen :: RandomGen tg => (Int,Int) -> Int-> tg -> ([Int], tg)
seqAndGen range count rng0 =
if (count <= 0)
then ([],rng0)
else
let (v,rng1) = randomR range rng0
nextSeq = seqAndGen range (count-1) rng1 -- recursive call
in
(v:(fst nextSeq), snd nextSeq)
-- returns the "field" values AND the final state of the generator
fieldAndGen :: RandomGen tg => Int -> tg -> ([(Int,Int,Int)], tg)
fieldAndGen n rng0 =
let field0 = [(l,r) | l <- [0..n], r <- [0..n]]
range = (0,3) -- at that level, range gets hardwired
count = (n+1)*(n+1) -- number of field/board positions
pair = seqAndGen range count rng0 -- the hard work
vSeq = fst pair
endRng = snd pair
flatten = \((a,b),c) -> (a,b,c)
field = map flatten (zip field0 vSeq)
in
(field, endRng)
主要程序:
main = do
let mySeed = 42
n = 5
putStrLn $ "seed=" ++ (show mySeed) ++ " " ++ "n=" ++ (show n)
-- get a random number generator:
let rng0 = mkStdGen mySeed
let (field, endRng) = fieldAndGen n rng0
fieldv = map (\(a,b,c) -> c) field
putStrLn $ "endRng = " ++ (show endRng)
putStrLn $ "field = " ++ (show field)
程序输出:
seed=42 n=5
endRng = 1388741923 1700779863
field = [(0,0,1),(0,1,1),(0,2,3),(0,3,0),(0,4,2),(0,5,1),(1,0,0),(1,1,1),(1,2,0),(1,3,1),(1,4,3),(1,5,1),(2,0,2),(2,1,0),(2,2,2),(2,3,3),(2,4,1),(2,5,1),(3,0,3),(3,1,2),(3,2,0),(3,3,2),(3,4,2),(3,5,0),(4,0,2),(4,1,0),(4,2,0),(4,3,0),(4,4,1),(4,5,0),(5,0,2),(5,1,1),(5,2,0),(5,3,2),(5,4,0),(5,5,1)]
请注意,存在一种可能的变体,即不传递生成器,而是传递由函数生成的 v 值的无限列表randomRs https://hackage.haskell.org/package/random-1.1/docs/System-Random.html#v:randomRs。使用功能很方便splitAt https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:splitAt为了这样的目的。
但这假设您仅对 v 值使用随机性,而不使用其他任何东西,因此它不太通用且不太灵活。