Haskell: 单子的单子
Haskell: Monads of monad
我正在学习一些 Haskell 并且我在使用这些 Monad 时遇到了一些麻烦,我了解它们并且知道它们是关于什么的,但是在这种特殊情况下我遇到了一些问题。在 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')
这行得通,如果我将我的位置提供给此函数,它会成功计算出未来可能的位置,但现在我希望在其中实现 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
它就可以工作,但是使用 monad 我不能从 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'
returns 一个 KnightRoute
的列表,我需要一个 KnightRoute
,这是一个逻辑错误,但我不知道该怎么做。有没有一种简单的方法可以用 Monad 做到这一点?
提前致谢:)
这种“两个monad的组合”在Haskell中是极其常见的事情。幸运的是,该语言足够灵活,我们可以很好地对此进行抽象。
从数学上讲,您想要的是 composition of two functors. Instead of that newtype, this is usually expressed with the concept of transformers: instead of using the Writer
monad directly, you use the WriterT
monad transformer. WriterT w [] a
is basically the same 作为 [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')
你可以写
a' :: Int -> KnightRoute
a' i = a >>= \p -> moveKnight' p !! i
这里i
用来剔除写手的内表。而且,由于懒惰,您可以将 Int -> a
变成 [a]
:
asList :: (Int -> a) -> [a]
asList f = map f [1..]
那么a'
的所有路由列表是
a's :: [KnightRoute]
a's = asList a'
把所有东西放在一起:
moveKnight :: KnightRoute -> [KnightRoute]
moveKnight k = map (\i -> k >>= \p -> moveKnight' p !! i) [1..]
我正在学习一些 Haskell 并且我在使用这些 Monad 时遇到了一些麻烦,我了解它们并且知道它们是关于什么的,但是在这种特殊情况下我遇到了一些问题。在 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')
这行得通,如果我将我的位置提供给此函数,它会成功计算出未来可能的位置,但现在我希望在其中实现 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
它就可以工作,但是使用 monad 我不能从 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'
returns 一个 KnightRoute
的列表,我需要一个 KnightRoute
,这是一个逻辑错误,但我不知道该怎么做。有没有一种简单的方法可以用 Monad 做到这一点?
提前致谢:)
这种“两个monad的组合”在Haskell中是极其常见的事情。幸运的是,该语言足够灵活,我们可以很好地对此进行抽象。
从数学上讲,您想要的是 composition of two functors. Instead of that newtype, this is usually expressed with the concept of transformers: instead of using the Writer
monad directly, you use the WriterT
monad transformer. WriterT w [] a
is basically the same 作为 [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')
你可以写
a' :: Int -> KnightRoute
a' i = a >>= \p -> moveKnight' p !! i
这里i
用来剔除写手的内表。而且,由于懒惰,您可以将 Int -> a
变成 [a]
:
asList :: (Int -> a) -> [a]
asList f = map f [1..]
那么a'
的所有路由列表是
a's :: [KnightRoute]
a's = asList a'
把所有东西放在一起:
moveKnight :: KnightRoute -> [KnightRoute]
moveKnight k = map (\i -> k >>= \p -> moveKnight' p !! i) [1..]