我正在一个我们使用随机性和monad工作的实验室工作。
实验室的各个部分是:
我已经完成了1,2和3,但我遇到了4。
这里是给定的代码:
RandState.hs
module RandState where
import UCState
import System.Random
-- In order to generate pseudo-random numbers, need to pass around generator
-- state in State monad
type RandState a = State StdGen a
-- runRandom runs a RandState monad, given an initial random number generator
runRandom :: RandState a -> StdGen -> a
runRandom (State f) s = res
where (res, state) = f s
-- rand is a helper function that generates a random instance of any
-- type in the Random class, using the RandState monad.
rand :: Random a => RandState a
rand = do
gen <- get
let (x, gen') = random gen
put gen'
return x
UCState.hs
{-
- Simplified implementation of the State monad. The real implementation
- is in the Control.Monad.State module: using that is recommended for real
- programs.
-}
module UCState where
data State s a = State { runState :: s -> (a, s) }
instance Monad (State s)
where
{-
- return lifts a function x up into the state monad, turning it into
- a state function that just passes through the state it receives
-}
return x = State ( \s -> (x, s) )
{-
- The >>= combinator combines two functions p and f, and
- gives back a new function (Note: p is originally wrapped in the
- State monad)
-
- p: a function that takes the initial state (from right at the start
- of the monad chain), and gives back a new state and value,
- corresponding to the result of the chain up until this >>=
- f: a function representing the rest of the chain of >>='s
-}
(State p) >>= f = State ( \initState ->
let (res, newState) = p initState
(State g) = f res
in g newState )
-- Get the state
get :: State s s
get = State ( \s -> (s, s) )
-- Update the state
put :: s -> State s ()
put s = State ( \_ -> ((), s))
这是我的代码,我刚刚在RandState.hs中编写,因为我无法弄清楚如何导入它(导入的帮助也很好,虽然不是我所做的)最关心的是这一点):
randR :: Random a => (a, a) -> RandState a
randR (lo, hi) = do
gen <- get
let (x, gen') = randomR (lo, hi) gen
put gen'
return x
testRandR1 :: IO Bool
testRandR1 = do
gen <- newStdGen
let genR = runRandom (randR (1,5)) gen :: Int
return (genR <=5 && genR >=1)
testRandR2 :: IO Bool
testRandR2 = do
gen <- newStdGen
let genR = runRandom (randR (10.0, 11.5)) gen :: Double
return (genR <= 11.5 && genR >= 10.0)
rollTwoDice :: RandState Int
rollTwoDice = do
gen <- get
let (a, gen') = randomR (1, 6) gen :: (Int, StdGen)
put gen'
let (b, gen'') = randomR (1, 6) gen' :: (Int, StdGen)
put gen''
return $ a + b
testRollTwoDice :: IO Bool
testRollTwoDice = do
gen <- newStdGen
let genR = runRandom (rollTwoDice) gen
return (genR <= 12 && genR >= 2)
-- Data types to represent playing cards
data CardValue = King | Queen | Jack | NumberCard Int
deriving (Show, Eq)
data CardSuit = Hearts | Diamonds | Spades | Clubs
deriving (Show, Eq)
data PlayingCard = PlayingCard CardSuit CardValue
deriving (Show, Eq)
{-
- fullCardDeck will be a deck of cards, 52 in total, with a King, a Queen,
- a Jack and NumberCards from 1 to 10 for each suit.
-}
-- fullCardDeck and its definition were given in the lab
fullCardDeck :: [PlayingCard]
fullCardDeck = [ PlayingCard s v | s <- allsuits, v <- allvals ] where
allvals = King : Queen : Jack : [ NumberCard i | i <- [1..10] ]
allsuits = [Hearts, Diamonds, Spades, Clubs]
removeCard :: [a] -> RandState [a]
removeCard deck = do
gen <- get
let n = runRandom (randR(1, length (deck))) gen :: Int
let (xs, ys) = splitAt (n-1) deck
return $ head ys : xs ++ tail ys
shuffleDeck deck = do
gen <- get
let f deck = head $ runRandom (removeCard deck) gen
return $ take (length(deck)) (iterate f deck)
shuffleDeck不起作用。错误:
RandState.hs:88:31:
Occurs check: cannot construct the infinite type: a0 = [a0]
Expected type: [a0] -> [a0]
Actual type: [a0] -> a0
In the first argument of `iterate', namely `f'
In the second argument of `take', namely `(iterate f deck)'
In the second argument of `($)', namely `take 52 (iterate f deck)'
我想问题是iterate接受一个值,将函数应用于此值,将函数应用于结果,依此类推,返回无限的结果列表。我正在迭代一个带有列表的函数,并返回一张卡片,因此结果无法传递给下一次迭代。什么是解决这个问题的更好方法(4)?我还担心我的removeCard功能有点笨拙,因为它只是将&#34;删除&#34;卡在前面,我做了shuffleDeck更容易写。如果有必要,什么是更好的方法来解决这个问题(3)?
谢谢, 杰夫
答案 0 :(得分:1)
你应该停止在你的职能范围内尝试runRandom
。一旦你真的想要一个结果,你应该只使用runRandom
(例如 - 打印结果,因为你不能在monad中做到这一点)。试图从monad中“逃脱”是徒劳的任务,你只会产生令人困惑且经常无法运行的代码。所有函数的最终输出都将在monad中,因此无论如何都不需要转义。
请注意
gen <- get
let n = runRandom (randR(1, length (deck))) gen :: Int
完全等同于
n <- randR (1, length deck)
<-
语法在右侧的monad中执行计算,并将其“放入”左侧的变量名称。
改组:
shuffleR [] = return []
shuffleR xs = do
(y:ys) <- removeR xs -- 1
zs <- shuffleR ys -- 2
return (y:zs) -- 3
该函数是直接的递归: 1)提取随机元素,2)随机播放剩下的内容,3)合并结果。
编辑:请求额外信息:
randSum :: (Num b, Random b) => State StdGen b
randSum = do
a <- randR (1,6)
b <- randR (1,6)
return $ a + b
编译得很好。从您对错误的描述来判断,您试图在IO
monad中调用此函数。你不能混合monad(或至少不那么简单)。如果您想在RandState
内“执行”IO
类型的内容,则必须在此处使用runRandom
。
n <- randR (1, length deck)
使n
成为Int
,因为length deck
的类型为Int
和randR :: Random a => (a, a) -> RandState a
,因此从上下文我们可以推断{{1}并且类型统一到a ~ Int
。
回顾一下
错误:
(Int, Int) -> RandState Int
右:
try = do
a <- randomIO :: IO Int
b <- randR (0,10) :: RandState Int
return $ a + b -- monads don't match!