用Haskell中的typeclass表示游戏状态
我正在实现一些功能来搜索游戏树,以供自己娱乐。例如,一个这样的函数将执行算法。这些函数使用两种对象:用Haskell中的typeclass表示游戏状态,haskell,typeclass,Haskell,Typeclass,我正在实现一些功能来搜索游戏树,以供自己娱乐。例如,一个这样的函数将执行算法。这些函数使用两种对象: 游戏状态,例如棋盘上棋子的位置、下一个要移动的玩家等 移动,例如“典当e2到e5” 我的目标是拥有适用于不同游戏的搜索功能,因此我将游戏特定知识作为助手功能传递,如: 从给定游戏状态生成所有可能的移动 给定一个状态和一个移动,将移动应用于该状态以获得结果状态 看看这是否是一个胜利的位置 等等 这样,我的搜索功能确实不是游戏专用的,但解决方案仍然感觉不正确。这也使得我的参数列表相当长: --
- 从给定游戏状态生成所有可能的移动
- 给定一个状态和一个移动,将移动应用于该状态以获得结果状态
- 看看这是否是一个胜利的位置
- 等等
-- Given a game state find the best next move
bestMove :: s -> (s -> [m]) -> (s -> m -> s) -> m -- more parameters in my real code
bestMove currentState possibleMoves applyMove = ...
问题:
我可以用TypeClass替换这些助手函数吗?我在寻找与此类似的东西:
class Move m where
apply :: State s => s -> m -> s
class State s where
possibleMoves :: Move m => s -> [m]
bestMove :: State s => Move m => s -> m
bestMove currentState = ... -- e.g. = head $ possibleMoves state
问题是State
的任何实例都只能与Move
的一个特定实例一起工作,反之亦然:
{-# LANGUAGE InstanceSigs #-}
data ChessState = ...
data ChessMove = ...
instance Move ChessMove where
apply :: ChessState -> ChessMove -> ChessState
apply s m = ...
apply
的类型签名当然是错误的。应该是
apply :: S s => s -> ChessMove -> s
但是我确实需要特定于ChessState
的属性,以便创建ChessMove
我的整个想法是完全错误的,还是有办法对
ChessState
和ChessMove
之间的关系进行编码?我在这方面取得了一些进展,但看起来仍然不像我希望的那样。首先,如果你可以在没有类型类的情况下做一些事情,那么最好远离它们。您始终可以为特定应用程序制作一个更简单的包装器,特别是在适当翻转参数的情况下:
bestMove :: (s -> [m]) -> (m -> s -> s) -> s -> m
bestChessMove :: ChessState -> ChessMove
bestChessMove = bestMove possibleChessMoves applyChessMove
也就是说,我并不认为你所设想的类是不可感知的(不像很多人想挤进Haskell的OO类…)。您的思路非常正确,它应该是一个多内存类:
{-# LANGUAGE MultiParamTypeClasses #-}
class MoveState m s where
apply :: m -> s -> s
possibleMoves :: s -> [m]
instance MoveState ChessMove ChessState where
apply = ...
possibleMoves = ...
然后
事实上,该类的通用性远远超过了它的意义:对于任何给定的状态类型,您可能只有一种移动类型。表达这一点的一种方式是a|s->m
;我通常倾向于将Move
类型简单化为state类的“属性”:
{-# LANGUAGE TypeFamilies #-}
class GameState s where
type Move s :: *
apply :: Move s -> s -> s
possibleMoves :: s -> [Move s]
首先,如果您可以在没有类型类的情况下做一些事情,那么最好远离它们。您始终可以为特定应用程序制作一个更简单的包装器,特别是在适当翻转参数的情况下:
bestMove :: (s -> [m]) -> (m -> s -> s) -> s -> m
bestChessMove :: ChessState -> ChessMove
bestChessMove = bestMove possibleChessMoves applyChessMove
也就是说,我并不认为你所设想的类是不可感知的(不像很多人想挤进Haskell的OO类…)。您的思路非常正确,它应该是一个多内存类:
{-# LANGUAGE MultiParamTypeClasses #-}
class MoveState m s where
apply :: m -> s -> s
possibleMoves :: s -> [m]
instance MoveState ChessMove ChessState where
apply = ...
possibleMoves = ...
然后
事实上,该类的通用性远远超过了它的意义:对于任何给定的状态类型,您可能只有一种移动类型。表达这一点的一种方式是a|s->m
;我通常倾向于将Move
类型简单化为state类的“属性”:
{-# LANGUAGE TypeFamilies #-}
class GameState s where
type Move s :: *
apply :: Move s -> s -> s
possibleMoves :: s -> [Move s]
你可以考虑只通过整个游戏树:
data GameTree m s = GameTree s [(m, GameTree m s)]
bestMove :: GameTree m s -> (s -> Int) -> Maybe m
bestMove gameTree evaluateState = ...
依靠懒惰只扩展你实际观察到的游戏树的部分。
< P>你可以考虑在整个游戏树中通过:data GameTree m s = GameTree s [(m, GameTree m s)]
bestMove :: GameTree m s -> (s -> Int) -> Maybe m
bestMove gameTree evaluateState = ...
依靠懒惰只扩展游戏树中你实际看到的部分。我很难在这个答案和我接受的答案之间做出决定。我觉得这个答案描述了一个更好的解决方案,但对于从不同背景遇到这个问题的人来说可能没有那么有用。我很难在这个答案和我接受的答案之间做出决定。我觉得这个答案描述了一个更好的解决方案,但对于从不同背景遇到这个问题的人来说可能没有那么有用。