我遇到过这段代码,但我不知道grid <- get
做了什么?我们怎么知道这个网格是我们当前的网格?我们并没有把它作为一个论点。那么我们如何使用它来获取行呢?
data Grid = Grid [Row]
type GridState a = State Grid a
initializeGrid :: GridState ()
initializeGrid = do
setPositionToColor 2 0 Alive
setPositionToColor :: Int -> Int -> CellState -> GridState ()
setPositionToColor x y color = do
grid <- get
let rows = getRows grid
...
put newState
getRows :: Grid -> [Row]
...
答案 0 :(得分:5)
State
monad实际上隐藏了这样一个事实,即每个函数都将状态值作为输入,并在输出中包含(可能已更改)状态。 get
实际上只返回隐藏的参数。
如果您在不使用State
的情况下编写函数,情况会更清楚。
-- State Grid a == Grid -> (Grid, a)
setPositionToColor :: Int -> Int -> Color -> Grid -> (Grid, ())
^^^^^^^^^^^^^^^^^^
当您致电setPositionToColor 2 0 Alive
时,您实际上并未为网格中的任何特定元素着色,因为尚未涉及网格。您只需返回一个函数,当使用Grid
调用 it 时,将生成新修改的Grid
。
如果没有Monad
实例,每次调用setPositionToColor
都需要额外的参数,并且它会返回一个新的Grid
以传递给下一个调用。您的代码看起来像
let (grid1,_) = setPositionToColor x1 y1 color1 initialGrid
(grid2,_) = setPositionToColor x2 y2 color2 grid1
(grid3,_) = setPositionToColor x3 y3 color3 grid2
(grid4,_) = setPositionToColor x4 y4 color4 grid3
in grid4
所有Monad
实例都要注意将中间Grid
值从一个函数传递给下一个函数;您需要做的只是提供initialGrid
作为runState
的参数,它实际上开始调用撰写的State
操作。
-- Back to setPositionToColor :: Int -> Int -> Color -> State Grid a
let allFour = setPositionToColor x1 y1 color1 >>= (\() ->
setPositionToColor x2 y2 color2 >>= (\() ->
setPositionToColor x3 y3 color3 >>= (\() ->
setPositionToColor x3 y3 color4)))
in runState allFour initialGrid
或者,因为我们实际上并不关心每次通话返回的()
值,
let allFour = setPositionToColor x1 y1 color1 >>
setPositionToColor x2 y2 color2 >>
setPositionToColor x3 y3 color3 >>
setPositionToColor x3 y3 color4
in runState allFour initialGrid
使用do
表示法,
let allFour = do
setPosition x1 y1 color1
setPosition x2 y2 color2
setPosition x3 y3 color3
setPosition x4 y4 color4
in runState allFour initialGrid
答案 1 :(得分:3)
您的网格已“包含”在State
monad中。了解您的GridState a
是如何定义的?它是State Grid a
的别名。它说GridState
是一个状态monad,它“携带”Grid
类型的状态。
State
monad“包含”你的网格的方式也相对简单:你可以look it up,它只是一个以状态为参数的函数。这个monad中的计算只是通过步骤“隧道化”这个参数,但这种情况发生在幕后,对你来说是无形的。编译器将do
语法转义为>>=
运算符的连续应用程序,对于State
monad,此运算符将状态参数“隧道”从一个计算到下一个计算。
get
函数是State
monad中的monadic操作。它只返回monad中“包含”的状态(你可以look up the function too)。
因此,阅读第grid <- get
行的方式是“取出State
monad的当前状态,并将其命名为grid
”。