我正在Haskell的国际象棋比赛中工作,我正在努力移动我的作品。
我理解在函数式编程中,一切都应该是不可变的,但我认为我真的需要有一个更新的片段列表。我看了monad.state
,但我很难理解它。
这是我的作品清单:
piecesList::[Piece]
piecesList = [Piece _type _color _coords, ..., ... Piece _type _color _coords]
我的方法是将作品从(old_x,old_y)
移动到(new_x,new_y)
:
在我的列表中找到(old_x,old_y)
作为坐标的文章:
piece = getPiece (index_of (old_x old_y))
与
getPiece::Int->Piece
getPiece a = piecesList!!a
和
index_of :: (Int,Int)->Int
index_of (old_x, old_y) = fromJust $ findIndex piece piecesList
where
piece (Piece _ _ pos) = pos == (old_x, old_y)
更新此特定作品的坐标:
moved = move (piece (new_x,new_y))
与
move::Piece->(Int,Int)->Piece
move piece (new_x,new_y) = piece { _position = (new_x,new_y) }
使用以下内容更新作品列表:
piecesList = updateBoard (index_of a b ) moved
与
updateBoard :: Int -> Piece -> Maybe [Piece]
updateBoard index newPiece = return board
where
(x,_:ys) = splitAt index piecesList
board = x ++ newPiece : ys
但是,看起来我的作品列表没有更新。
我接近吗?如果是这样,我错过了什么?或者我的做法完全错了?
谢谢!
修改
我使用以下类型:
data Piece = Piece {
_type :: PieceType,
_color :: PieceColor,
_position :: Position
} deriving Eq
data PieceColor = Black | White deriving Eq
data PieceType = Rook | Knight | Bishop | King | Queen | Pawn deriving Eq
type Position = (Int, Int)
答案 0 :(得分:1)
看起来我的作品列表没有更新。
当然不是:像Haskell中的所有内容一样,片段列表是 immutable ,因此在任何情况下它都不会改变。
使用
piecesList = updateBoard (index_of a b ) moved
您只需定义一个 new 片段列表,这些片段恰好也称为pieces
。 GHCi和IHaskell允许这种阴影(Haskell本身不会!),但它只是意味着您之后定义的任何内容引用piecesList
将使用新版本。但getPiece
和index_of
已经在此“更新”之前已经定义,并且完全无视您稍后选择提供的任何新定义。
完成此类任务的最直接方法是明确传递游戏状态的修改版。实际上,updateBoard
已经通过提供整个[Piece]
列表作为结果来实现这一目标。但是您还需要在下一步中使用更新状态,而不是再次启动状态piecesList
。基本上,如果您只是将pieces
作为参数传递给getPiece
,index_of
和updateBoard
,您就可以完成任务了。
updateBoard :: Int -> Piece -> [Piece] -> [Piece] -- You don't seem to need `Maybe`
请注意,此签名被解析为
updateBoard :: Int -> Piece -> ([Piece] -> [Piece])
现在,它有点尴尬,不得不明确地给各种辅助函数赋予相同的旧值。你已经提到了 state monad ,这确实是在这里使用的标准内容。本质上,状态monad完全相同:将一个值作为参数传递给子函数。唯一的区别是,如果没有另外说明,它会自动使用相同的值。
您将签名更改为
import Control.Monad.State
updateBoard :: Int -> Piece -> State [Piece] ()
此处,State [Piece] ()
只是[Piece] -> ([Piece], ())
的新类型包装器。 ()
表示除了更新状态之外,您不会提供任何有趣的结果信息。你可以提供其他信息,确实需要在getPieces
和indexOf
中提供:
getPiece :: Int -> State [Piece] Piece
indexOf :: (Int,Int) -> State [Piece] Int
现在关于如何实际编写所有内容:do
表示法有帮助。只需将结果放在return
的末尾,然后使用get
获取旧状态。例如,
getPiece :: Int -> State [Piece] Piece
getPiece a = do
piecesList <- get
return $ piecesList!!a
新状态可以简单地“put
进入monad”:
updateBoard :: Int -> Piece -> State [Piece] ()
updateBoard index newPiece = do
piecesList <- get
let (x,_:ys) = splitAt index piecesList
put $ x ++ newPiece : ys