此时我们没有关于 Card 和 Deck 数据类型的信息。
然而,目前的问题似乎是随机从最初的 N 张牌中提取 M 张牌。
如果这个问题的解释是正确的,那么我们可以使用Rand monad 构造函数,并首先定义一个单子动作,将一张牌从右卡组转移到左卡组。
由于我们没有关于使用的类型的信息,我们将假设“卡片”用普通数字表示,从 0 到 51。
接下来,我们定义一个动作递归地移动 M 张牌,移动一张牌,然后用 (M-1) 个参数调用我们自己。对于 M=0,我们将操作定义为无操作。
这将是一元代码:
import System.Random
import Control.Monad.Random
moveOneCardLeft :: RandomGen g => ([a],[a]) -> Rand g ([a],[a])
moveOneCardLeft (deck, rest) =
do
let remCount = length rest
choice <- getRandomR (0, (remCount-1))
let (top, bot) = splitAt choice rest
return $ ((head bot) : deck, top ++ (tail bot))
moveSomeCardsLeft :: RandomGen g => Int -> ([a],[a]) -> Rand g ([a],[a])
moveSomeCardsLeft 0 (deck, rest) = return (deck, rest) -- unchanged
moveSomeCardsLeft n (deck, rest) =
do
(deck1, rest1) <- moveOneCardLeft (deck, rest)
(deck2, rest2) <- moveSomeCardsLeft (n-1) (deck1, rest1)
return (deck2, rest2)
extractSomeCards :: RandomGen g => Int -> [a] -> Rand g ([a], [a])
extractSomeCards n xs =
do
(deck, rest) <- moveSomeCardsLeft n ([], xs)
return (deck, rest)
接下来是纯代码和一些暂定的游戏相关实用功能:
drawSomeCards :: RandomGen g => g -> Int -> [a] -> (([a], [a]), g)
drawSomeCards gen0 n xs = runRand (extractSomeCards n xs) gen0
cardValue :: Int -> Int
cardValue n = let rank = mod n 13
in if (rank < 10) then (rank+1)
else {- Jack Queen King -} 10
deckValue :: [Int] -> Int
deckValue cards = sum (map cardValue cards)
totalOfCards :: [Int] -> Maybe Int
totalOfCards cards =
let s = deckValue cards
in if (s <= 21) then (Just s) else Nothing
最后是用户测试代码:
main = do
let wholeDeck = [0..51]
randomSeed = 4243
gen0 = mkStdGen randomSeed
putStrLn "How many cards ?"
inLine <- getLine
let count = (read inLine :: Int)
putStrLn $ "Want to extract " ++ (show count) ++ " cards."
let ((deck, rest), gen1) = drawSomeCards gen0 count wholeDeck
sumw = sum wholeDeck
suma = sum deck
sumb = sum rest
sum0 = (suma + sumb) - sumw
putStrLn $ "Must be zero: " ++ (show sum0) -- sanity check
putStrLn $ "deck: " ++ (show deck)
putStrLn $ "rest: " ++ (show rest)
putStrLn $ "Deck value: " ++ (show $ deckValue deck)
程序执行:
$ q67025780.x
How many cards ?
10
Want to extract 10 cards.
Must be zero: 0
deck: [8,47,38,49,4,31,9,30,28,23]
rest: [0,1,2,3,5,6,7,10,11,12,13,14,15,16,17,18,19,20,21,22,24,25,26,27,29,32,33,34,35,36,37,39,40,41,42,43,44,45,46,48,50,51]
Deck value: 77
$
注意:如果认为合适,可以使用Control.Monad.HT 包中的nest :: Monad m => Int -> (a -> m a) -> a -> m a 函数简化moveOneCardLeft 之外的上述代码。
像这样:
import Control.Monad.HT (nest)
moveOneCardLeft :: RandomGen g => ([a],[a]) -> Rand g ([a],[a])
moveOneCardLeft (deck, rest) =
do
let remCount = length rest
choice <- getRandomR (0, (remCount-1))
let (top, bot) = splitAt choice rest
return $ ((head bot) : deck, top ++ (tail bot))
drawSomeCards :: RandomGen g => g -> Int -> [a] -> (([a], [a]), g)
drawSomeCards gen0 n xs = let action = nest n moveOneCardLeft ([], xs)
in runRand action gen0