2010-05-16 12 views
7

Wie kann ich mehrere Anrufe an SDL.pollEvent :: IO Event bis die Ausgabe SDL.NoEvent Ausgabe und sammeln Sie alle Ergebnisse in eine Liste?Sammeln von IO-Ausgaben in Liste

In Imperativ Begriffe etwas wie folgt aus:

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 aus der "Liste" -Paket auf Hackage:

events = [] 
event = SDL.pollEvent 
while (event != SDL.NoEvent) { 
     events.add(event) 
     event = SDL.pollEvent 
} 

Antwort

4

James Cook war so freundlich monad-loop s mit dieser Funktion zu erweitern:

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

verwendet mit SDL:

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

Sie monadischen Listen verwenden können.

+0

Warum "repeat()"? – Peaker

+1

@peaker: 'repeat() :: ListT IO()' ist eine unendliche IO-monadische Liste mit Werten, die keine Rolle spielen ('()'). dann '' '' '' 'mit pollEvent', so dass wir für jedes Element der unendlichen Liste' pollEvent'. 'takeWhile' macht es zu einer endlichen monadischen Liste und dann macht 'toList' es' :: IO [Event] '. – yairchu

+0

Das scheint ein bisschen komisch. Vielleicht macht es mehr Sinn, etwas wie "repeatM (lift pollEvent)" zu verwenden? – Peaker

4

könnten Sie so etwas wie verwenden:

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

Statt:

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

Sie können auch verwenden:

liftM (x:) (takeWhileM p act) wodurch man

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

Dann können Sie verwenden : takeWhileM (/=SDL.NoEvent) SDL.pollEvent

+0

da sein. Ich würde 'takeUntilM :: Monad m => (a -> Bool) -> ma -> m [a]' (mit passender 'return [x]' wenn 'px'' false' ist, um Informationsverlust zu vermeiden (besonders von IO-Monaden). Das kann normal aussehen, wenn es nur "SDL.NoEvent" ist, aber es könnte falsch sein für "Left" Systemabsturz :: Entweder String a'. – ony

+0

Oh, und Sie wollen wahrscheinlich eine faule Liste erstellen, also müssen Sie 'interleaveIO' von' System.Unsafe' (oder so ähnlich) verwenden. I.e. etwas wie 'liftM (x :) (interleaveIO (unsafeTakeUntilM p act))' – ony

+1

Viele Varianten von 'takeWhileM': http://stackoverflow.com/questions/1133800/haskell-monadic-takewhile/1138153#1138153 – kennytm

0

i btw von 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) 

Dank für Ihre Antworten in einem aktuellen SDL Spiel über diesen Code-Schnipsel schließlich gestolpert!

+1

Wenn das ist so beliebt Ansatz, um alle Ereignisse in der Warteschlange in einem Schuss, ohne es zu verarbeiten, ziehen, als, warum SDL API bietet es nicht direkt? Das kann helfen, eine Synchronisierung zu vermeiden. Overhead für thread-sichere Warteschlange. – ony

1

Mit diesem Stubs für Event und pollEvent

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

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

pollEvent :: IO Event 
pollEvent = randomIO 

und einen Kombinator, entlehnt und von an earlier answer angepasst, die das erste Mal hört Auswertung das Prädikat

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] 

ermöglicht diese GHCI Sitzung fehlschlägt, für Beispiel:

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

sehr Neuling freundliche Version, danke auch :) – user341228