2010-05-16 4 views
7

Как я могу выдать несколько звонков на номер SDL.pollEvent :: IO Event до выхода SDL.NoEvent и собрать все результаты в список?Сбор выходов IO в список

В императивных условиях что-то вроде этого:

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

ответ

4

Джеймс Кук был настолько любезен, чтобы расширить monad-loop с с помощью этой функции:

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

используется с SDL:

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

Почему "повторить()"? – Peaker

+1

@peaker: 'repeat() :: ListT IO()' - бесконечный IO-монадический список, содержащий значения, которые не имеют значения ('()'). то мы '(>>)' it с 'lift pollEvent', так что для каждого элемента бесконечного списка мы' pollEvent'. 'takeWhile' делает его конечным монадическим списком, а затем' toList' делает его ':: IO [Event]'. – yairchu

+0

Это кажется немного странным. Может быть, имеет смысл использовать что-то вроде «repeatM (lift pollEvent)»? – Peaker

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] '(с соответствующий 'return [x]', когда 'px' является' false'), чтобы избежать потери информации (особенно из IO-монад). Это может выглядеть нормально, когда это просто «SDL.NoEvent», но это может быть неправильно для «левой» сбой системы »:: Либо String a'. – ony

+0

О, и вы, вероятно, хотите создать ленивый список, поэтому вам нужно использовать 'interleaveIO' из' System.Небезопасный (или что-то в этом роде). То есть что-то вроде 'liftM (x :) (interleaveIO (unsafeTakeUntilM p act))' – ony

+1

Множество вариантов 'takeWhileM': http://stackoverflow.com/questions/1133800/haskell-monadic-takewhile/1138153#1138153 – kennytm

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

1

Используя эти заглушки для Event и pollEvent

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