Ако разбирам правилно въпроса, трябва да има една произволна стойност на (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 въпроса. Можете да използвате местната търсачка. Ето например един от последните.
Ами ако трябва да върнете своя генератор за повторна употреба?
В такъв случай трябва да имате функция, която взема предварително изграден генератор и връща И ДВАТА списъка с триплети "поле" и (крайното състояние на) генератора като двойка (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 стойности и нищо друго, така че е малко по-малко общо и по-малко гъвкаво.
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