Если я правильно понимаю вопрос, должно быть одно случайное значение для каждой (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. Давайте используем сеанс 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. Вы можете использовать местную поисковую систему. Вот, например, один из последних.
Что делать, если вам нужно вернуть генератор для повторного использования?
В этом случае вам нужна функция, которая принимает предварительно созданный генератор и возвращает ОБА список триплетов «поля» и генератор (конечное состояние) в виде пары (список, 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 и ничего больше, так что это немного менее общее и менее гибкое.
person
jpmarinier
schedule
22.02.2020
vs
и определитьfield n vs = [ ... , v <- vs]
? - person Thomas M. DuBuisson   schedule 22.02.2020rows * cols * vs
вместо желаемых значенийrows * cols
. - person MikaelF   schedule 23.02.2020