哈斯克尔:洗牌

时间:2013-11-14 00:31:38

标签: loops haskell random shuffle

我正在一个我们使用随机性和monad工作的实验室工作。

实验室的各个部分是:

  1. 编写一个在给定范围内生成随机数的函数randR
  2. 编写一个模拟滚动两个骰子的函数rollTwoDice
  3. 编写一个函数removeCard,随机从PlayCards列表中删除一张卡
  4. 写一个函数shuffleDeck,它取出被移走的卡片,放在卡片前面,然后重复自己,直到卡片完全洗牌。
  5. 我已经完成了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)?

    谢谢, 杰夫

1 个答案:

答案 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的类型为IntrandR :: 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!