我正在学习一些Haskell,我对这些Monads有些麻烦,我了解它们并知道它们是什么,但在这种特殊情况下我有一些问题.在LYAH学习的时候,我遇到了一个练习,这个练习是关于计算你可以用骑士(来自国际象棋游戏)进行3次动作的位置,我们使用这样的列表monad:
假设,
type KnightPos = (Int,Int)
moveKnight :: KnightPos -> [KnightPos]
moveKnight (c,r) = do
(c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
]
guard (c' `elem` [1..8] && r' `elem` [1..8])
return (c',r')
Run Code Online (Sandbox Code Playgroud)
这是有效的,如果我给这个函数我的位置,它可以很好地计算未来可能的位置,但现在我正在寻找实现其中的Writer monad所以我可以检索我是如何达到这一点的.所以我做了这个功能,
假设,
type KnightRoute = Writer [KnightPos] KnightPos
moveKnight' :: KnightPos -> [KnightRoute]
moveKnight' (c,r) = do
(c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
]
guard (c' `elem` [1..8] && r' `elem` [1..8])
return $ toKr (c',r') (c,r)
where toKr pos oldpos = Writer (pos,[oldpos])
Run Code Online (Sandbox Code Playgroud)
它可以工作,如果我给它一个KnightPos但使用monads我无法KnightPos从a中提取a KnightRoute来执行该函数...
*Main> let a = moveKnight' (2,4) !! 0
*Main> runWriter a
((4,3),[(2,4)])
*Main> a >>= moveKnight'
<interactive>:4:7:
Couldn't match type ‘[]’ with ‘Writer [KnightPos]’
Expected type: KnightPos -> Writer [KnightPos] KnightRoute
Actual type: KnightPos -> [KnightRoute]
In the second argument of ‘(>>=)’, namely ‘moveKnight'’
In the expression: a >>= moveKnight'
Run Code Online (Sandbox Code Playgroud)
我明白为什么它不起作用,我(4,3)从我的作家中提取出来然后我把它给了KnightPos'.但是KnightPos'返回一个KnightRoute我需要的列表KnightRoute,这是逻辑错误,但我不知道该怎么办.使用Monads有一种简单的方法吗?
先谢谢:)
这种"两个monad的组合"在Haskell中是非常常见的.幸运的是,语言足够灵活,我们可以很好地抽象出来.
在数学上说你想要的是两个仿函数的组合.这通常用变换器的概念来表示,而不是Writer直接使用monad,而是使用WriterTmonad变换器.WriterT w [] a是基本相同的[Writer w a],所以你的情况,你可以使用:
import Control.Monad.Trans.Class
import Control.Monad.Trans.Writer
moveKnight'' :: KnightPos -> WriterT [] [KnightPos] KnightPos
moveKnight'' (c,r) = do
(c',r') <- lift [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
]
guard (c' `elem` [1..8] && r' `elem` [1..8])
tell [(c,r)]
return (c',r')
Run Code Online (Sandbox Code Playgroud)