2012-05-31 6 views
9

Ho un compito semplice: leggere un sacco di righe da un file e fare qualcosa con ognuna di esse. Tranne il primo - che sono alcuni titoli da ignorare.Come utilizzare la funzione di caduta conduit in una tubazione?

Quindi ho pensato di provare i conduit.

printFile src = runResourceT $ CB.sourceFile src =$= 
    CT.decode CT.utf8 =$= CT.lines =$= CL.mapM_ putStrLn 

Freddo.

Quindi ora voglio solo far cadere la prima linea di off ... e sembra che ci sia una funzione per questo -

printFile src = runResourceT $ CB.sourceFile src =$= 
    CT.decode CT.utf8 =$= CT.lines =$= drop 1 =$= CL.mapM_ putStrLn 

Hmm - ma ora mi accorgo goccia è di tipo firma Sink a m(). Qualcuno mi ha suggerito che posso usare l'istanza Monade per tubi e utilizzo goccia a goccia effectfully alcuni elementi - così ho provato questo:

drop' :: Int -> Pipe a a m() 
drop' n = do 
    CL.drop n 
    x <- await 
    case x of 
    Just v -> yield v 
    Nothing -> return() 

Il che non tipo si verifica perché l'istanza Monade per tubi si applica solo ai tubi dello stesso tipo - I sink hanno Void come output, quindi non posso usarlo in questo modo.

Ho dato una rapida occhiata a pipe e pipe-core e ho notato che il core-core ha la funzione che mi aspettavo che fosse, dove come pipe è una libreria minima ma la documentazione mostra come sarebbe implementata.

Così mi sono confuso - forse c'è un concetto chiave che mi manca .. Ho visto la funzione

sequence :: Sink input m output -> Conduit input m output 

Ma questo non sembra essere l'idea giusta, in quanto il valore di uscita è ()

CL.sequence (CL.drop 1) :: Conduit a m()  

io probabilmente basta andare indietro e uso pigro-io, come non ho davvero bisogno di alcun streaming - ma sarei interessato a vedere il modo corretto di farlo.

risposta

6

In primo luogo, la risposta è semplice:

... =$= CT.lines =$= (CL.drop 1 >> CL.mapM_ putStrLn) 

La spiegazione più: ci sono davvero due modi diversi è possibile implementare drop. In entrambi i casi, per prima cosa verranno rilasciati gli elementi n dall'input. Ci sono due scelte su ciò che fa successivo:

  • dice che è fatto
  • Inizio emettere tutti gli elementi rimanenti dal flusso di input

L'ex comportamento è quello che sarebbe effettuare una Sink (e cosa fa in realtà il nostro drop mentre il secondo è il comportamento di un Conduit. infatti è possibile generare il secondo dal primo attraverso composizione monadica:

dropConduit n = CL.drop n >> CL.map id 

quindi è possibile utilizzare dropConduit come lei all'inizio. Questo è un buon modo per dimostrare la differenza tra composizione monadica e fusione; il primo consente a due funzioni di operare sullo stesso flusso di input, mentre il secondo consente a una funzione di alimentare un flusso all'altro.

Non ho un benchmark, ma sono abbastanza certo che la composizione monadica sarà un po 'più efficiente.

+0

Hmm - la risposta semplice funziona bene, grazie. dropConduit è 'Monad m => Int -> Pipe Void Void m()' che ritengo sia piuttosto difficile da usare per qualsiasi cosa io pensi? – Oliver

+0

Siamo spiacenti, sto lavorando a una versione diversa della base di codici in cui ciò non si applica. In conduit 0.4, avresti bisogno di avere 'sinkToPipe (CL.drop n) >> CL.map id'. Il problema è che i tipi in Data.Conduit.List sono eccessivamente restrittivi. il conduit 0.5 li rilasserà. –

+0

Ahh - evviva. Ciò ha senso. – Oliver