簡體   English   中英

哈斯克爾:monad monad

[英]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但使用的單子我不能提取KnightPosKnightRoute執行功能的其他時間...

*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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM