コードだけが必要な場合は、次のとおりです。
procFile' iFile oFile = fileDriver (joinI $
enumLinesBS ><>
mapChunks (map rstrip) $
I.mapM_ (B.appendFile oFile))
iFile
解説:
これは 3 段階のプロセスです。まず生のストリームを行のストリームに変換し、関数を適用してその行のストリームを変換し、最後にストリームを消費します。rstrip
は中盤なので、ストリームトランスフォーマー(Enumeratee)を作っていきます。
または のいずれmapChunks
かを使用できますconvStream
が、mapChunks
より簡単です。違いは、より一般的なmapChunks
のに対し、チャンクの境界を越えることができないことです。基礎となる実装を公開しないため、convStream
私は好みますが、十分な場合、結果のコードは通常より短くなります。convStream
mapChunks
rstripE :: Monad m => Enumeratee [ByteString] [ByteString] m a
rstripE = mapChunks (map rstrip)
の余分な点に注意してmap
くださいrstripE
。外側のストリーム (rstrip への入力) は type[ByteString]
であるため、それにマップrstrip
する必要があります。
比較のために、convStream で実装した場合は次のようになります。
rstripE' :: Enumeratee [ByteString] [ByteString] m a
rstripE' = convStream $ do
mLine <- I.peek
maybe (return B.empty) (\line -> I.drop 1 >> return (rstrip line)) mLine
これはより長く、より多くの行が使用できる場合でも、rstrip 関数を一度に 1 行にしか適用しないため、効率が低下します。mapChunks
バージョンに近い、現在利用可能なすべてのチャンクで作業することが可能です。
rstripE'2 :: Enumeratee [ByteString] [ByteString] m a
rstripE'2 = convStream (liftM (map rstrip) getChunk)
とにかく、ストリッピング enumeratee が利用できるので、enumLinesBS
enumeratee で簡単に合成できます:
enumStripLines :: Monad m => Enumeratee ByteString [ByteString] m a
enumStripLines = enumLinesBS ><> rstripE
合成演算子><>
は、矢印演算子と同じ順序に従います>>>
。 enumLinesBS
ストリームを行に分割してから、それらをrstripE
取り除きます。これで、コンシューマー (通常の iteratee) を追加するだけで完了です。
writer :: FilePath -> Iteratee [ByteString] IO ()
writer fp = I.mapM_ (B.appendFile fp)
processFile iFile oFile =
enumFile defaultBufSize iFile (joinI $ enumStripLines $ writer oFile) >>= run
関数は、単純にファイルを列挙し、結果の iteratee を実行するためのfileDriver
ショートカットです (残念ながら、引数の順序は enumFile から切り替えられます)。
procFile2 iFile oFile = fileDriver (joinI $ enumStripLines $ writer oFile) iFile
補遺: これは、convStream の追加機能が必要になる状況です。2 行ごとに 1 行に連結するとします。使用できませんmapChunks
。チャンクがシングルトン要素である場合を検討してください[bytestring]
。 mapChunks
次のチャンクにアクセスする方法を提供しないため、これと連結するものは他にありません。ただし、それはconvStream
簡単です:
concatPairs = convStream $ do
line1 <- I.head
line2 <- I.head
return $ line1 `B.append` line2
これは適用可能なスタイルでさらに見栄えがよく、
convStream $ B.append <$> I.head <*> I.head
convStream
提供された iteratee を使用してストリームの一部を継続的に消費し、変換されたバージョンを内部のコンシューマーに送信すると考えることができます。各ステップで同じ iteratee が呼び出されるため、これでも一般的ではない場合があります。その場合、unfoldConvStream
連続する反復間で状態を渡すために使用できます。
convStream
またunfoldConvStream
、ストリーム処理 iteratee はモナド トランスフォーマーであるため、モナド アクションも可能です。