Conduitソースからコレクションを作成する
Conduitの存在意義は何といっても、データ全体をメモリに乗せる事なく、読んだ端から処理していくストリーム処理です。 しかし世の中には、オンメモリでないと不可能、またはオンメモリの方が効率的な処理というのもあります。
例えば、以下のようなものです。
2パス以上のアクセスが必要な操作(偏差値の算出など)
複数回の添字アクセスや検索(一回だけならtakeやdrop、filterで対象要素を抽出する事で実現可能) Conduit使用時にこれらを実現したい場合、ソースを一旦消費して、メモリ上にコレクションのデータ構造を作成する事になります。
以下、コード例では次のインポートを仮定します。
code:haskell
ghci> import Data.Conduit
ghci> import qualified Data.Conduit.Combinators as C
ghci> import qualified Data.Conduit.List as L
リスト
code:haskell
ghci> l <- runConduit $ L.sourceList 1..10 .| C.sinkList ghci> print l
リスト化してもシーケンシャルアクセスくらいしか出来ませんが、シーケンシャルアクセスならばソースを直接消費した方が効率的。なので、基本的にアンチパターンです。
既存のリスト処理にどうしても渡したい場合など?
配列
sinkVectorで可能。ジェネリックに定義されているため、BoxedだけでなくUnboxedやStorableのVectorも作れます。 code:haskell
ghci> import Data.Vector.Unboxed as U
ghci> v <- runConduit $ L.sourceList 1..10 .| C.sinkVector :: IO (U.Vector Int) ghci> v U.! 3
4
メモリ確保は適当にまとめてくれるのでパフォーマンスも安心。要素数が事前に分かっている場合はsinkVectorNにする事でさらにメモリ確保回数を減らせます。
添字アクセスやソートはこれで。
マップ
以下のようなシンクを定義します。
code:haskell
ghci> import qualified Data.Map.Strict as Map
ghci> sinkMap = C.foldMap $ \(k, v) -> Map.singleton k v
(k, v)の値のソースを消費して、マップが作れます。
code:haskell
ghci> l = [(k, v) | k <- 1..10, v <- show k] ghci> m <- runConduit $ L.sourceList l .| sinkMap
ghci> Map.lookup 1 m
Just '1'
検索したい場合はこれ。