[英]Haskell: Monads of monad
我正在學習一些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')
這是有效的,如果我給這個函數我的位置,它可以很好地計算未來可能的位置,但現在我正在尋找實現其中的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
但使用的單子我不能提取KnightPos
從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'
我理解為什么它不起作用,我從我的作家中提取(4,3)
然后我把它交給KnightPos'
。 但KnightPos'
收益清單KnightRoute
,我需要一個KnightRoute
,這是邏輯的錯誤,但我不知道該怎么辦。 使用Monads有一種簡單的方法嗎?
先謝謝:)
這種“兩個monad的組合”在Haskell中是非常常見的。 幸運的是,語言足夠靈活,我們可以很好地抽象出來。
在數學上說你想要的是兩個仿函數的組合 。 這通常用變換器的概念來表示,而不是直接使用Writer
monad,而是使用WriterT
monad變換器。 WriterT w [] a
與 [Writer wa]
基本相同 ,所以在你的情況下你可以使用:
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..]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.