You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell中为骰子分配场景实现State Monad及函子等实例

自定义State Monad实现与骰子属性状态管理

1. 核心State类型定义

按照Graham Hutton的思路,先定义State monad的基础类型:

newtype State s a = State { runState :: s -> (a, s) }
  • s代表状态的类型(这里我们用[Int]表示剩余骰子池)
  • a代表计算返回的结果类型
  • runState函数用于执行State计算,传入初始状态后返回结果和更新后的状态

2. Functor实例实现

instance Functor (State s) where
  fmap f (State sa) = State $ \s -> let (a, s') = sa s in (f a, s')

将函数f映射到State计算的结果上,状态传递保持不变。

3. Applicative实例实现

instance Applicative (State s) where
  pure a = State $ \s -> (a, s)
  (State sf) <*> (State sa) = State $ \s ->
    let (f, s') = sf s
        (a, s'') = sa s'
    in (f a, s'')
  • pure创建一个不修改状态的计算,直接返回给定值
  • <*>先执行生成函数的State计算,再用新状态执行生成值的计算,最后将函数应用到值上

4. Monad实例实现

instance Monad (State s) where
  return = pure
  (State sa) >>= f = State $ \s ->
    let (a, s') = sa s
        (State sb) = f a
    in sb s'
  • return同pure,用于包装值为State计算
  • >>=(绑定操作)先执行第一个State计算得到结果和新状态,再用该结果生成第二个State计算,最后在新状态上执行第二个计算

5. 实用状态操作辅助函数

定义两个常用的状态操作函数简化开发:

-- 获取当前状态
get :: State s s
get = State $ \s -> (s, s)

-- 更新状态
put :: s -> State s ()
put s = State $ \_ -> ((), s)

6. 骰子属性分配逻辑实现

我们用纯函数模拟属性分配,忽略IO交互部分:

-- 定义角色属性类型
data Attribute = Strength | Dexterity | Constitution deriving (Show)
type AttributeValue = Int
type DicePool = [Int] -- 剩余骰子池类型

-- 分配n个骰子到指定属性,返回属性及总和,自动更新骰子池状态
allocateAttribute :: Attribute -> Int -> State DicePool (Attribute, AttributeValue)
allocateAttribute attr n = do
  currentDice <- get
  let (usedDice, remainingDice) = splitAt n currentDice
      total = sum usedDice
  put remainingDice
  return (attr, total)

7. 使用示例

模拟初始骰子池,执行属性分配序列:

-- 模拟掷出的初始骰子结果
initialDice :: DicePool
initialDice = [4, 6, 3, 5, 2, 4]

-- 定义完整的属性分配流程
assignAttributes :: State DicePool [(Attribute, AttributeValue)]
assignAttributes = do
  strength <- allocateAttribute Strength 3
  dexterity <- allocateAttribute Dexterity 2
  constitution <- allocateAttribute Constitution 1
  return [strength, dexterity, constitution]

-- 运行State计算并查看结果
main :: IO ()
main = print $ runState assignAttributes initialDice

执行后会输出:

([(Strength,13),(Dexterity,7),(Constitution,4)],[])

可以看到State monad自动管理了骰子池的状态,无需手动传递剩余骰子。

内容的提问来源于stack exchange,提问作者Yago

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 12:12:22