Если я правильно понимаю вопрос, то на каждую позицию доски должно быть одно случайное значение. Таким образом, проблема не может быть решена путем добавления третьего предложения в список понимания, такого как:
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 . Давайте используем сеанс 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
почти решает нашу проблему, за исключением того, что мы не получаем точно триплеты:
λ>
λ> 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. Возможно, вы захотите начать с здесь или там .
Также, как упоминалось Томасом М. Дюбюссоном, этот вопрос был рассмотрен в нескольких SO-вопросах. Вы можете использовать местную поисковую систему. Вот один из последних , например.
Что если вам нужно вернуть генератор для повторного использования?
В этом случае вам нужна функция который берет предварительно созданный генератор и возвращает ОБА список «триплетов» и (конечное состояние) генератора в виде пары (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 . Для этой цели удобно использовать функцию splitAt . Но это предполагает, что вы используете случайность только для значений v и ничего больше, поэтому он немного менее общий и менее гибкий.