我正在学习Haskell,对于Monad这个概念我有些困惑。虽然我知道它是什么,但在某些情况下我还是遇到了问题。在学习LYAH时,我遇到了一个练习,要计算骑士(象棋中的角色)在3次移动后所能到达的位置。我们使用了列表Monad,如下所示:
假设:
这个函数可行,如果我把我的位置传给这个函数,它就可以成功地计算出可能的未来位置,但现在我想在其中实现Writer monad,以便我可以获取到达此点的详细信息。 所以我写了这个函数,
假设,
我明白为什么它不起作用了,我从我的Writer中提取了
提前感谢 :)
假设:
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')
这个函数可行,如果我把我的位置传给这个函数,它就可以成功地计算出可能的未来位置,但现在我想在其中实现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])
如果我提供一个KnightPos
,它可以工作,但使用单子,我无法从KnightRoute
中提取KnightPos
以便另一次执行该函数...
*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'
我明白为什么它不起作用了,我从我的Writer中提取了
(4,3)
,但是我将其传递给了KnightPos'
。但是KnightPos'
返回一个KnightRoute
列表,而我需要一个KnightRoute
,这是一个逻辑错误,但我不知道该怎么做。是否有一种使用Monads的简单方法来解决这个问题?提前感谢 :)
Writer
或WriterT
与列表作为日志单子是相当低效的(二次时间),因为您不断地向右添加。差异列表或更专业的容器是更好的选择。 - leftaroundabout