Перечисление / ветвление перечисляемого - PullRequest
2 голосов
/ 05 мая 2011

Я использую enumerator-0.4.10, и мне нужно распределить обработку разные части входящего потока для разных итераторов (я парсинг огромного файла XML, и разные поддеревья имеют разные логика обработки). Только один итератор будет активным одновременно поскольку поддеревья не пересекаются.

Я написал простой пример, который фильтрует поток и передает результат одному итератору; пожалуйста, смотрите ниже. Однако с несколькими вложенными мне кажется, что я больше не могу использовать перечисляемого. Я нужно написать свой собственный multi-enumeratee, который содержит несколько внутренних iteratees? Есть идеи получше?

Вот мой (начинающий) код для одного вложенного итератора:

module Main ( main ) where

import qualified Data.Enumerator as E ( Enumeratee, Step(..), Stream(..),
  checkDone, checkDoneEx, continue, enumList, joinI, run_, yield )
import Data.Enumerator ( ($$), (>>==) )
import qualified Data.Enumerator.List as EL ( consume )

-- cribbed from EL.concatMap
concatMapAccum :: Monad m => (s -> ao -> (s, [ai])) -> s ->
E.Enumeratee ao ai m b
concatMapAccum f s0 = E.checkDone (E.continue . step s0)
  where
    step _ k E.EOF = E.yield (E.Continue k) E.EOF
    step s k (E.Chunks xs) = loop s k xs
    loop s k [] = E.continue (step s k)
    loop s k (x:xs) = case f s x of
      (s', ais) -> k (E.Chunks $ ais) >>==
        E.checkDoneEx (E.Chunks xs) (\k' -> loop s' k' xs)

passFromTo :: Monad m => ((a -> Bool), (a -> Bool)) -> Bool -> E.Enumeratee a a m b
passFromTo (from, to) pass0 =
  concatMapAccum updatePass pass0
    where
      updatePass pass el = case (pass, from el, to el) of
        (True, _, to_el) -> (not to_el, [el])
        (False, True, _) -> (True, [el])
        (False, False, _) -> (False, [])

main :: IO()
main = do
  E.run_ (E.enumList 3 [1..20] $$
    E.joinI $ passFromTo ((\e -> e == 3 || e == 13), (\e -> e == 7 || e == 17)) False $$
    EL.consume) >>= print

$ ./dist/build/StatefulEnumeratee/StatefulEnumeratee
[3,4,5,6,7,13,14,15,16,17]

1 Ответ

1 голос
/ 29 августа 2011

Да, вам нужен перечислитель, который передает поток нескольким итераторам, например Data.Iteratee.sequence_ и Data.Iteratee.Parallel.psequence_ из iteratee-0.8.6. sequence_ принимает список итераторов для одновременной работы и обрабатывает каждый входной блок на mapM по всему этому списку. psequence_ принимает аналогичные аргументы, но запускает каждого итератора ввода в отдельном потоке forkIO.

В течение прошедшего года было несколько дискуссий о haskell-cafe и списках рассылки итерированных, например: http://www.haskell.org/pipermail/haskell-cafe/2011-January/088319.html Главное, на что следует обращать внимание, это обработка ошибок внутренних итераторов: в вашем приложении , если один внутренний итерирующий выходит из строя, хотите ли вы прекратить все итераторы или только этот, и [как] вы хотите распространить эти ошибки.

...