2010-05-16 11 views

Odpowiedz

4

James Cook był tak uprzejmy, aby przedłużyć monad-loop S z tej funkcji:

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

stosować SDL:

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

Można użyć monadycznego list:

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 z "listy" pakiet na hackage.

+0

Dlaczego "repeat()"? – Peaker

+1

@peaker: 'repeat() :: ListT IO()' jest nieskończoną listą IO-monadyczną zawierającą wartości, które nie mają znaczenia ('()'). następnie '(>>)' to z 'lift pollEvent', tak aby dla każdego elementu nieskończonej listy' pollEvent'. 'takeWhile' czyni z niego skończoną listę monadyczną, a następnie' toList' czyni ją ':: IO [Event]'. – yairchu

+0

To wydaje się nieco dziwne. Może bardziej sensowne byłoby użycie czegoś takiego jak "repeatM (lift pollEvent)"? – Peaker

4

Można użyć coś takiego:

 
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 [] 

zamiast:

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

można również użyć:

liftM (x:) (takeWhileM p act) otrzymując:

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

Następnie można użyć : takeWhileM (/=SDL.NoEvent) SDL.pollEvent

+0

Proponuję' takeUntilM :: Monad m => (a -> Bool) -> ma -> m [a] '(z odpowiedni "return [x]", gdy 'px' jest' false'), aby uniknąć utraty informacji (zwłaszcza z monad IO). Może to wyglądać normalnie, gdy jest to po prostu 'SDL.NoEvent', ale może to być złe dla' Left 'awarii systemu ":: Either String a'. – ony

+0

Aha, i prawdopodobnie chcesz zbudować leniwą listę, więc musisz użyć 'interleaveIO' z' System.Unsafe' (lub coś w tym stylu). To znaczy. coś takiego jak 'liftM (x :) (interleaveIO (unsafeTakeUntilM p act))' – ony

+1

Wiele wariantów 'takeWhileM': http://stackoverflow.com/questions/1133800/haskell-monadic-takewhile/1138153#1138153 – kennytm

0

I w końcu potknął się o tym fragmencie kodu w rzeczywistej grze SDL z 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) 

btw dzięki za odpowiedzi!

+1

Jeśli jest to tak popularne podejście do wyciągania wszystkich zdarzeń oczekujących w kolejce w jednym ujęciu bez przetwarzania go, to dlaczego API SDL nie udostępnia go bezpośrednio? To może pomóc uniknąć synchronizacji. narzut na kolejkę wątkową. – ony

1

Stosując te odcinki dla Event i pollEvent

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

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

pollEvent :: IO Event 
pollEvent = randomIO 

i COMBINATOR, pożyczone i adaptacją an earlier answer, że przestaje oceny po raz pierwszy orzecznik zawiedzie

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] 

Pozwala to sesja ghci, dla przykład:

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

bardzo przyjazna wersja dla nowicjuszy, dziękuję też :) – user341228

Powiązane problemy