2010-05-16 45 views

回答

4

詹姆斯·庫克是這麼樣使用此功能擴展monad-loop S:

events <- unfoldWhileM (/= SDL.NoEvent) SDL.pollEvent 
2

你可以用一元列表:

import Control.Monad.ListT (ListT) 
import Control.Monad.Trans.Class (lift) -- transformers, not mtl 
import Data.List.Class (takeWhile, repeat, toList) 
import Prelude hiding (takeWhile, repeat) 

getEvents :: IO [Event] 
getEvents = 
    toList . takeWhile (/= NoEvent) $ do 
     repeat() 
     lift pollEvent :: ListT IO Event 

ListT從hackage 「列表」 包。

+0

爲什麼 「重複()」:

unfoldWhileM :: Monad m => (a -> Bool) -> m a -> m [a] 

與SDL使用? – Peaker 2010-05-16 15:22:08

+1

@peaker:'repeat():: ListT IO()'是包含無關緊要的值的無限IO單子列表('()')。那麼我們用'lift pollEvent'調用'(>>)',這樣對於無限列表中的每個元素,我們都使用'pollEvent'。 'takeWhile'使其成爲一個有限單子列表,然後'toList'使其成爲':: IO [Event]'。 – yairchu 2010-05-16 15:54:03

+0

這似乎有點怪異..也許更有意義的使用像「repeatM(lift pollEvent)」? – Peaker 2010-05-17 06:29:11

4

您可以使用類似:

 
takeWhileM :: (a -> Bool) -> IO a -> IO [a] 
takeWhileM p act = do 
    x <- act 
    if p x 
    then do 
     xs <- takeWhileM p act 
     return (x : xs) 
    else 
     return [] 

相反的:

do 
    xs <- takeWhileM p act 
    return (x : xs) 

你也可以使用:

liftM (x:) (takeWhileM p act)產生:

 
takeWhileM :: (a -> Bool) -> IO a -> IO [a] 
takeWhileM p act = do 
    x <- act 
    if p x 
    then liftM (x:) (takeWhileM p act) 
    else return [] 

然後你可以使用:takeWhileM (/=SDL.NoEvent) SDL.pollEvent

+0

我建議'takeUntilM :: Monad m =>(a - > Bool) - > ma - > m [a]'(with當'px'爲'false'時適當的'return [x]')以避免信息丟失(尤其是IO單元)。當它只是'SDL.NoEvent'時,這可能看起來很正常,但是對於「Left」系統崩潰可能是錯誤的「:: String a'。 – ony 2010-05-16 17:49:03

+0

哦,你可能想構建懶惰列表,所以需要使用'System.Unsafe'中的'interleaveIO'(或類似的東西)。即例如'liftM(x :)(interleaveIO(unsafeTakeUntilM p act))' – ony 2010-05-16 17:56:13

+1

'takeWhileM'的很多變體:http://stackoverflow.com/questions/1133800/haskell-monadic-takewhile/1138153#1138153 – kennytm 2010-05-16 18:47:49

0

我最終絆倒在實際的遊戲SDL此代碼段從hackage

getEvents :: IO Event -> [Event] -> IO [Event] 
getEvents pEvent es = do 
    e <- pEvent 
    let hasEvent = e /= NoEvent 
    if hasEvent 
    then getEvents pEvent (e:es) 
    else return (reverse es) 

謝謝您的回答順便說一句!

+1

如果這是非常受歡迎的方法,可以在不處理它的情況下一次性排出隊列中的所有事件,而不是爲什麼SDL API不直接提供它?這可能有助於避免某些同步。線程安全隊列的開銷。 – ony 2010-05-17 13:06:47

1

使用這些存根EventpollEvent

data Event = NoEvent | SomeEvent 
    deriving (Show,Eq) 

instance Random Event where 
    randomIO = randomRIO (0,1) >>= return . ([NoEvent,SomeEvent] !!) 

pollEvent :: IO Event 
pollEvent = randomIO 

和組合子,貸款以及從an earlier answer調整,即停止評估首次謂詞失敗

spanM :: (Monad m) => (a -> Bool) -> m a -> m [a] 
spanM p a = do 
    x <- a 
    if p x then do xs <- spanM p a 
       return (x:xs) 
     else return [x] 

允許這個ghci的會議,爲例如:

*Main> spanM (/= NoEvent) pollEvent 
[SomeEvent,SomeEvent,NoEvent]
+0

非常新手友好的版本,也謝謝你:) – user341228 2010-05-17 08:09:10