代码之家  ›  专栏  ›  技术社区  ›  dopatraman

如何使用单子洗牌列表?

  •  0
  • dopatraman  · 技术社区  · 6 年前

    我有以下数据类型:

    data Card = Card One | Card Two | ...
    data Deck = Deck [Card]
    

    我想洗牌

    import System.Random
    
    shuffle :: Deck -> Deck
    shuffle (Deck c) = Deck $ shuffleOnce randomN c
        where   randomN = randomIO >>= (\x -> return (x `mod` 52))
                shuffleOnce n c = (findNth n c : deleteNth n c)
                findNth 0 (x:_) = x
                findNth n (_:xs) = findNth (n-1) xs
                deleteNth 0 (_:xs) = xs
                deleteNth n (x:xs) = x : (deleteNth (n-1) xs)
    

    问题(显然)在这里:

            where   randomN = randomIO >>= (\x -> return (x `mod` 52))
    

    我不知道如何使用 IO

    或者更好的问题是,我该怎么洗牌呢?

    请帮忙。完整代码 here

    1 回复  |  直到 6 年前
        1
  •  2
  •   Igor Drozdov    6 年前

    编译器说问题出在 (randomN 1) . randomN 函数是用参数调用的,但它不接受参数。

    然后它就不知道应该是哪种类型的随机数,所以我们需要在这里提供一个:

    randomIO >>= (\x -> return (x `mod` 52)) :: IO Int
    

    那么 No instance for (Eq (IO Int)) Int 是意料之中的,但是 IO Int 提供。为了方便起见,我们可以交换 shuffleOnce n c

    shuffleOnce c <$> randomN
    

    之后,错误显示:

    Expected type: [Card]
    Actual type: IO [Card]
    

    我们需要利用 <$> 再次代替 $ ,只是为了“进去” IO :

    Deck <$> shuffleOnce c <$> randomN
    

    Couldn't match expected type ‘Deck’ with actual type ‘IO Deck’
    

    我们对此无能为力,但要改变 shuffle 功能:

    shuffle :: Deck -> IO Deck
    shuffle (Deck c) = Deck <$> shuffleOnce c <$> randomN
        where   randomN = randomIO >>= (\x -> return (x `mod` 52)) :: IO Int
                shuffleOnce c n = (findNth n c : deleteNth n c)
                findNth 0 (x:_) = x
                findNth n (_:xs) = findNth (n-1) xs
                deleteNth 0 (_:xs) = xs
                deleteNth n (x:xs) = x : (deleteNth (n-1) xs)
    

    洗牌