我相信在 Haskell 中,可以通过为掷骰子过程找到最合适的数据类型来实现惯用方式。
那是什么..?我们将掷n 骰子m 次。该算法需要玫瑰树(N-ary tree)数据类型。比如
data Rolls = Dice :< [Rolls] deriving Show
你已经在Data.Tree 包中拥有了这个,但是为了用更简单的术语表达逻辑,我将尝试使用上述数据非常简化的 N 叉树类型。通过展开使用标准 Data.Tree 包来实现这一点很简单。
这也是非常有效的,因为我们在不可能的路线发生之前消除了它们。所以这里没有低效的组合和过滤。
type Count = Int
type Total = Int
type Value = Int
type Dice = (Count, Total, Value)
data Rolls = Dice :< [Rolls] deriving Show
rollDices :: Dice -> Int -> Int -> Rolls
rollDices (c,t,v) tc tt | c <= tc-2 = let bl = (\x -> if x < 1 then 1 else x) ((tt-t)-(tc-c-1)*6) -- bottom limit
tl = (tt-t)-(tc-c)+1 -- top limit
bs = if bl <= 6 then if tl <= 6 then [bl..tl] -- branches
else [bl..6]
else []
in (c,t,v) :< map (\n -> rollDices (c+1, t+n, n) tc tt) bs
| otherwise = (c,t,v) :< [(c+1,tt, tt-t) :< []]
main :: IO String
main = do
putStr "How many dice are to be rolled..? :"
tc <- (read :: String -> Int) <$> getLine
putStr "To what sum do you want to reach..? :"
tt <- (read :: String -> Int) <$> getLine
return . show $ rollDices (0,0,0) tc tt
这将计算一个具有 m 级的 N 叉树,并且每个路径都将保存除根(种子)为 0 的骰子值。所以让我们看看 4 个骰子的目标总和为 10。
*Main> main
How many dice are to be rolled..? :4
To what sum do you want to reach..? :10
"(0,0,0) :< [(1,1,1) :< [(2,2,1) :< [(3,4,2) :< [(4,10,6) :< []],(3,5,3) :< [(4,10,5) :< []],(3,6,4) :< [(4,10,4) :< []],(3,7,5) :< [(4,10,3) :< []],(3,8,6) :< [(4,10,2) :< []]],(2,3,2) :< [(3,4,1) :< [(4,10,6) :< []],(3,5,2) :< [(4,10,5) :< []],(3,6,3) :< [(4,10,4) :< []],(3,7,4) :< [(4,10,3) :< []],(3,8,5) :< [(4,10,2) :< []],(3,9,6) :< [(4,10,1) :< []]],(2,4,3) :< [(3,5,1) :< [(4,10,5) :< []],(3,6,2) :< [(4,10,4) :< []],(3,7,3) :< [(4,10,3) :< []],(3,8,4) :< [(4,10,2) :< []],(3,9,5) :< [(4,10,1) :< []]],(2,5,4) :< [(3,6,1) :< [(4,10,4) :< []],(3,7,2) :< [(4,10,3) :< []],(3,8,3) :< [(4,10,2) :< []],(3,9,4) :< [(4,10,1) :< []]],(2,6,5) :< [(3,7,1) :< [(4,10,3) :< []],(3,8,2) :< [(4,10,2) :< []],(3,9,3) :< [(4,10,1) :< []]],(2,7,6) :< [(3,8,1) :< [(4,10,2) :< []],(3,9,2) :< [(4,10,1) :< []]]],(1,2,2) :< [(2,3,1) :< [(3,4,1) :< [(4,10,6) :< []],(3,5,2) :< [(4,10,5) :< []],(3,6,3) :< [(4,10,4) :< []],(3,7,4) :< [(4,10,3) :< []],(3,8,5) :< [(4,10,2) :< []],(3,9,6) :< [(4,10,1) :< []]],(2,4,2) :< [(3,5,1) :< [(4,10,5) :< []],(3,6,2) :< [(4,10,4) :< []],(3,7,3) :< [(4,10,3) :< []],(3,8,4) :< [(4,10,2) :< []],(3,9,5) :< [(4,10,1) :< []]],(2,5,3) :< [(3,6,1) :< [(4,10,4) :< []],(3,7,2) :< [(4,10,3) :< []],(3,8,3) :< [(4,10,2) :< []],(3,9,4) :< [(4,10,1) :< []]],(2,6,4) :< [(3,7,1) :< [(4,10,3) :< []],(3,8,2) :< [(4,10,2) :< []],(3,9,3) :< [(4,10,1) :< []]],(2,7,5) :< [(3,8,1) :< [(4,10,2) :< []],(3,9,2) :< [(4,10,1) :< []]],(2,8,6) :< [(3,9,1) :< [(4,10,1) :< []]]],(1,3,3) :< [(2,4,1) :< [(3,5,1) :< [(4,10,5) :< []],(3,6,2) :< [(4,10,4) :< []],(3,7,3) :< [(4,10,3) :< []],(3,8,4) :< [(4,10,2) :< []],(3,9,5) :< [(4,10,1) :< []]],(2,5,2) :< [(3,6,1) :< [(4,10,4) :< []],(3,7,2) :< [(4,10,3) :< []],(3,8,3) :< [(4,10,2) :< []],(3,9,4) :< [(4,10,1) :< []]],(2,6,3) :< [(3,7,1) :< [(4,10,3) :< []],(3,8,2) :< [(4,10,2) :< []],(3,9,3) :< [(4,10,1) :< []]],(2,7,4) :< [(3,8,1) :< [(4,10,2) :< []],(3,9,2) :< [(4,10,1) :< []]],(2,8,5) :< [(3,9,1) :< [(4,10,1) :< []]]],(1,4,4) :< [(2,5,1) :< [(3,6,1) :< [(4,10,4) :< []],(3,7,2) :< [(4,10,3) :< []],(3,8,3) :< [(4,10,2) :< []],(3,9,4) :< [(4,10,1) :< []]],(2,6,2) :< [(3,7,1) :< [(4,10,3) :< []],(3,8,2) :< [(4,10,2) :< []],(3,9,3) :< [(4,10,1) :< []]],(2,7,3) :< [(3,8,1) :< [(4,10,2) :< []],(3,9,2) :< [(4,10,1) :< []]],(2,8,4) :< [(3,9,1) :< [(4,10,1) :< []]]],(1,5,5) :< [(2,6,1) :< [(3,7,1) :< [(4,10,3) :< []],(3,8,2) :< [(4,10,2) :< []],(3,9,3) :< [(4,10,1) :< []]],(2,7,2) :< [(3,8,1) :< [(4,10,2) :< []],(3,9,2) :< [(4,10,1) :< []]],(2,8,3) :< [(3,9,1) :< [(4,10,1) :< []]]],(1,6,6) :< [(2,7,1) :< [(3,8,1) :< [(4,10,2) :< []],(3,9,2) :< [(4,10,1) :< []]],(2,8,2) :< [(3,9,1) :< [(4,10,1) :< []]]]]"
所以上面我们有树表示。有时间我会写一个toList 函数。