4

私は特定のデータベースを使用しています。クエリが成功すると、特定のコマンドを使用して、結果のデータのチャンクのグループにアクセスできます。

getResultData :: IO (ResponseCode, ByteString)

getResultDataは、応答コードと、応答コードが次のようになっているデータを返します。

response = GET_DATA_FAILED | OPERATION_SUCCEEDED | NO_MORE_DATA

ByteStringは、チャンクの1つ、一部、またはすべてです。

データhttp://desmond.imageshack.us/Himg189/scaled.php?server=189&filename=chunksjpeg.png&res=medium

話はここで終わりではありません。グループのストリームが存在します:

ストリームhttp://desmond.imageshack.us/Himg695/scaled.php?server=695&filename=chunkgroupsjpeg.png&res=medium

getResultDataからNO_MORE_DATA応答を受信すると、getNextItemを呼び出すとストリームが繰り返され、getResultDataの呼び出しを再開できるようになります。getNextItemがSTREAM_FINISHEDを返すと、彼女が書いたのはそれだけです。私は自分のデータを持っています。

ここで、この現象をDate.IterateeまたはData.Enumeratorのいずれかで改造したいと思います。私の既存のData.Iterateeソリューションは機能しますが、それでも非常に単純なように見えます。現在のソリューションの実装方法である1つの大きなiterateeブロブではなく、ネストされた反復でこれをモデル化する必要があるように感じます。

私はData.Iteratee0.8.6.2のコードを見てきましたが、ネストされたものに関しては少し混乱しています。

ネストされたものは適切な行動方針を繰り返しますか?もしそうなら、ネストされた反復でこれをどのようにモデル化するでしょうか?

よろしく

4

1 に答える 1

3

ネストされた反復は正しいアプローチだと思いますが、この場合にはいくつかの固有の問題があり、ほとんどの一般的な例とは少し異なります。

チャンクとグループ

最初の問題は、データソースを正しく取得することです。基本的に、あなたが説明した論理的な分割はあなたに同等のストリームを与えるでしょう[[ByteString]]。これを直接生成する列挙子を作成する場合、ストリーム内の各要素はチャンクの完全なグループになります。これはおそらく(メモリ上の理由から)避けたいものです。すべてを1つにフラット化することもできますが[ByteString]、境界を再導入する必要があります。これは、dbが自動的に行うため、かなり無駄になります。

今のところグループのストリームを無視すると、データを自分でチャンクに分割する必要があるようです。私はこれを次のようにモデル化します:

enumGroup :: Enumerator ByteString IO a
enumGroup = enumFromCallback cb ()
 where
  cb () = do
    (code, data) <- getResultData
    case code of
        OPERATION_SUCCEEDED -> return $ Right ((True, ()), data)
        NO_MORE_DATA        -> return $ Right ((False, ()), data)
        GET_DATA_FAILED     -> return $ Left MyException

チャンクは固定サイズであるため、これを。で簡単にチャンクできますData.Iteratee.group

enumGroupChunked :: Iteratee [ByteString] IO a -> IO (Iteratee ByteString IO a)
enumGroupChunked = enumGroup . joinI . group groupSize

このタイプをと比較してくださいEnumerator

type Enumerator s m a = Iteratee s m a -> m (Iteratee s m a)

つまりenumGroupChunked、基本的には、ストリームタイプを変更する派手な列挙子です。これは、[ByteString] iterateeコンシューマーを受け取り、プレーンなバイトストリングを消費するiterateeを返すことを意味します。多くの場合、列挙子の戻りタイプは重要ではありません。runこれは、出力を取得するために(または)で評価する単なる反復子なtryRunので、ここでも同じことができます。

evalGroupChunked :: Iteratee [ByteString] IO a -> IO a
evalGroupChunked i = enumGroupChunked i >>= run

各グループでより複雑な処理を行う場合、最も簡単な場所はenumGroupChunked関数です。

グループのストリーム

さて、これは邪魔になりません、グループのストリームをどうするか?答えは、それらをどのように消費したいかによって異なります。基本的にストリーム内の各グループを個別に処理したい場合は、次のようなことを行います。

foldStream :: Iteratee [ByteString] IO a -> (b -> a -> b) -> b -> IO b
foldStream iter f acc0 = do
  val <- evalGroupChunked iter
  res <- getNextItem
  case res of 
        OPERATION_SUCCEEDED -> foldStream iter f $! f acc0 val
        NO_MORE_DATA        -> return $ f acc0 val
        GET_DATA_FAILED     -> error "had a problem"

ただし、個々のグループだけでなく、データセット全体に対して何らかのストリーム処理を実行したいとします。つまり、あなたは

bigProc :: Iteratee [ByteString] IO a

データセット全体に対して実行する必要があります。これは、列挙子の戻り反復が役立つ場所です。以前のコードの一部は、少し異なります。

enumGroupChunked' :: Iteratee [ByteString] IO a
  -> IO (Iteratee ByteString IO (Iteratee [ByteString] IO a))
enumGroupChunked' = enumGroup . group groupSize

procStream :: Iteratee [ByteString] IO a -> a
procStream iter = do
  i' <- enumGroupChunked' iter >>= run
  res <- getNextItem
  case res of 
        OPERATION_SUCCEEDED -> procStream i'
        NO_MORE_DATA        -> run i'
        GET_DATA_FAILED     -> error "had a problem"

ネストされた反復(つまりIteratee s1 m (Iteratee s2 m a))のこの使用法は少し一般的ではありませんが、複数の列挙子からのデータを順次処理する場合に特に役立ちます。重要なのはrun、外側のiterateeを実行すると、より多くのデータを受信する準備ができているiterateeが得られることを認識することです。これは、各グループを個別に列挙でき、それらを単一のストリームとして処理できるため、この場合にうまく機能するモデルです。

注意:内側の反復は、そのままの状態になります。グループの最後のチャンクが完全なチャンクよりも小さい可能性があるとします。

   Group A               Group B               Group C
   1024, 1024, 512       1024, 1024, 1024      1024, 1024, 1024

この場合、groupデータを1024のサイズのチャンクに結合するため、グループAの最後のチャンクとグループBの最初の512バイトが結合されます。このfoldStream例では、コードが終了するため、これは問題になりません。内側の反復(with joinI)。つまり、グループは本当に独立しているので、そのように扱う必要があります。のようにグループを結合する場合はprocStream、ストリーム全体を考える必要があります。これがあなたの場合であるならば、あなたはただより洗練された何かを使う必要があるでしょうgroup

Data.IterateeとData.Enumerator

IterIO (私は確かに偏っています)は言うまでもなく、どちらのパッケージのメリットについても議論することなく、2つのパッケージの最も重要な違いであるストリームの抽象化について指摘したいと思います。

Data.Iterateeでは、コンシューマーは、一度に1つIteratee ByteString m aのチャンクにアクセスできる、ある程度の長さの概念的なByteStringを操作しますByteString

Data.Enumeratorでは、コンシューマーIteratee ByteString m aは概念的な[ByteString]を操作し、一度に1つ以上の要素(バイト文字列)にアクセスします。

つまり、ほとんどのData.Iteratee操作は要素に焦点を合わせてIteratee ByteStringいます。つまり、単一ので操作しますがWord8、Data.Enumerator操作はチャンクに焦点を合わせて、を操作しByteStringます。

Data.Iteratee.Iteratee [s] m aあなたは===を考えることができますData.Enumerator.Iteratee s m a

于 2011-09-27T01:36:59.283 に答える