2011-07-14 11 views
6

Hoy temprano, escribí una pequeña aplicación de prueba para iterar que compuso una iteración para escribir el progreso con una iteración para copiar datos. Acabé con valores como los siguientes:Haskell enumerador: analog to iteratees `enumWith` operator?

-- NOTE: this snippet is with iteratees-0.8.5.0 
-- side effect: display progress on stdout 
displayProgress :: Iteratee ByteString IO() 

-- side effect: copy the bytestrings of Iteratee to Handle 
fileSink :: Handle -> Iteratee ByteString IO() 

writeAndDisplayProgress :: Handle -> Iteratee ByteString IO() 
writeAndDisplayProgress handle = sequence_ [fileSink handle, displayProgress] 

Al analizar la biblioteca empadronador, no veo un análogo de sequence_ o enumWith. Todo lo que quiero hacer es componer dos iteraciones para que actúen como una sola. Podría descartar el resultado (va a ser () de todos modos) o conservarlo, no me importa. (& & &) desde Control.Flecha es lo que quiero, solo para iterar en lugar de flechas.

me trataron estas dos opciones:

-- NOTE: this snippet is with enumerator-0.4.10 
run_ $ enumFile source $$ sequence_ [iterHandle handle, displayProgress] 
run_ $ enumFile source $$ sequence_ [displayProgress, iterHandle handle] 

El primero se copia el archivo, pero no muestra el progreso; el segundo muestra el progreso, pero no copia el archivo, así que obviamente el efecto de la secuencia incorporada en las iteraciones del enumerador es ejecutar el primer iterado hasta que termine y luego ejecutar el otro, que no es lo que quiero. Quiero ejecutar las iteraciones en paralelo en lugar de en serie. Siento que estoy perdiendo algo obvio, pero al leer el ejemplo wc para la biblioteca empadronador, veo este curioso comentario:

-- Exactly matching wc's output is too annoying, so this example 
-- will just print one line per file, and support counting at most 
-- one statistic per run 

Me pregunto si esta observación indica que la combinación o componer iteratees dentro del marco de las enumeraciones ISN posible fuera de la caja. ¿Cuál es la forma correcta y generalmente aceptada de hacer esto?

Editar:

Parece que no hay manera integrada para realizar esta acción. Hay un debate en la lista de correo de Haskell sobre la adición de combinadores como enumSequence y manyToOne, pero hasta el momento, no parece haber nada realmente en el paquete del enumerador que proporcione esta capacidad.

+0

no pasé el tiempo suficiente mirar la documentación para formular y probar una respuesta real, pero parece superficialmente como '' Enumerator's y Enumeratee's son componibles en la forma que desee. (PD: Supongo que hablas del paquete "enumerador" en Hackage. Realmente deberías decir algo así antes, porque ahora hay como media docena de implementaciones diferentes de iteratees. =) –

+0

También no gastaste suficiente tiempo leyendo mi pregunta –

+0

Eso es un poco grosero, considerando que acabo de pasar un poco de mi tiempo para ayudarte a resolver tu problema. En lugar de volverse sarcástico, tal vez podrías señalar la parte que piensas que extrañé. –

Respuesta

2

Me parece que en lugar de intentar que dos Iteratees consuman la secuencia en paralelo, sería mejor alimentar la secuencia a través de una identidad Enumeratee que simplemente cuente los bytes que la pasan.

Aquí hay un ejemplo simple que copia un archivo e imprime el número de bytes copiados después de cada fragmento.

import System.Environment 
import System.IO 
import Data.Enumerator 
import Data.Enumerator.Binary (enumFile, iterHandle) 
import Data.Enumerator.List (mapAccumM) 
import qualified Data.ByteString as B 

printBytes :: Enumeratee B.ByteString B.ByteString IO() 
printBytes = flip mapAccumM 0 $ \total bytes -> do 
    let total' = total + B.length bytes 
    print total' 
    return (total', bytes) 

copyFile s t = withBinaryFile t WriteMode $ \h -> do 
    run_ $ (enumFile s $= printBytes) $$ iterHandle h 

main = do 
    [source, target] <- getArgs 
    copyFile source target