在 Haskell 中移动棋子
Moving piece across a board in Haskell
我正在 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)
it looks like my list of pieces is not updated.
当然不是:就像 Haskell 中的所有内容一样,片段列表是 不可变的 ,因此它在任何情况下都不会改变。
有
piecesList = updateBoard (index_of a b ) moved
您只需定义一个 new 件列表,它也恰好被称为 pieces
。 GHCi 和 IHaskell 允许这种 shadowing(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
我正在 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)
it looks like my list of pieces is not updated.
当然不是:就像 Haskell 中的所有内容一样,片段列表是 不可变的 ,因此它在任何情况下都不会改变。
有
piecesList = updateBoard (index_of a b ) moved
您只需定义一个 new 件列表,它也恰好被称为 pieces
。 GHCi 和 IHaskell 允许这种 shadowing(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