在Haskell中移动一块板

时间:2017-04-09 15:42:33

标签: haskell global-variables monads

我正在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)

1 个答案:

答案 0 :(得分:1)

  

看起来我的作品列表没有更新。

当然不是:像Haskell中的所有内容一样,片段列表是 immutable ,因此在任何情况下它都不会改变。

使用

piecesList = updateBoard (index_of a b ) moved

您只需定义一个 new 片段列表,这些片段恰好也称为pieces。 GHCi和IHaskell允许这种阴影(Haskell本身不会!),但它只是意味着您之后定义的任何内容引用piecesList将使用新版本。但getPieceindex_of已经在此“更新”之前已经定义,并且完全无视您稍后选择提供的任何新定义。

完成此类任务的最直接方法是明确传递游戏状态的修改版。实际上,updateBoard已经通过提供整个[Piece]列表作为结果来实现这一目标。但是您还需要在下一步中使用更新状态,而不是再次启动状态piecesList。基本上,如果您只是将pieces作为参数传递给getPieceindex_ofupdateBoard,您就可以完成任务了。

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], ())的新类型包装器。 ()表示除了更新状态之外,您不会提供任何有趣的结果信息。你可以提供其他信息,确实需要在getPiecesindexOf中提供:

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