19

の定義Enumeratorは次のとおりです。

type Enumerator a m b = Step a m b -> Iteratee a m b

ドキュメントには、s がデータを消費している間Iteratee、s がデータをEnumerator生成すると記載されています。このようなタイプのデータを生成する方法を理解できます。

enumStream :: (Monad m) => Stream a -> Enumerator a m b
enumStream stream step =
    case step of
        Continue k -> k stream
        _          -> returnI step  -- Note: 'stream' is discarded

(はこれよりも複雑です...が与えられた後に がそうでないenumEOFことを確認し、そうである場合はエラーをスローするようです。)IterateeContinueEOF

つまり、で実行すると、 がIteratee生成されます。これは列挙子に供給され、続行できるように列挙子に供給されます。私の列挙子は結果の継続を返します。SteprunIterateeStepStream

Iteratee1 つ気になる点があります。このコードはモナドで実行されています。つまり、データを消費できるということですよね?

-- | Like 'enumStream', but consume and discard a chunk from the input stream
--   simply because we can.
enumStreamWeird :: (Monad m) => Stream a -> Enumerator a m b
enumStreamWeird stream step = do
    _ <- continue return            -- Look, mommy, I'm consuming input!
    case step of
        Continue k -> k stream
        _          -> returnI step

ドキュメントには、列挙子がソースとシンクの両方として機能する場合は、Enumeratee代わりに使用する必要があると記載されています。

type Enumeratee ao ai m b = Step ai m b -> Iteratee ao m (Step ai m b)

しかし、明らかにそうする必要はありませんでした。Enumerator関数で示されているように、 の定義で入力を使用できenumStreamWeirdます。

私の質問は次のとおりです。

  • Enumeratorのように 内でデータを「消費」しようとするとどうなりますenumStreamWeirdか? データはどこから来たのですか?

  • 列挙子でデータを消費するほど狂っていなくても、生成しているデータを読み取る iteratee に代わってではなく、列挙子に代わって基礎となるモナドでアクションを実行することは有効ですか?

後者の質問は私の主な質問とはあまり関係がないかもしれませんが、 anEnumeratorがどのように機能するかを理解しようとしています。

4

2 に答える 2

3

はい、列挙子はデータを消費できます。列挙子は基本的に iteratee を取り、いくつかの項目が供給された後に同じ iteratee に変換します。列挙子が入力を求める場合、結果の iteratee は入力を求めます。

Enumerator が Iteratee に供給される方法

列挙子が iteratee に与えられる方法を見てみましょう:

-- | Feed an Enumerator to an Iteratee.
feed :: Monad m
     => Iteratee a m b
     -> Enumerator a m b
     -> Iteratee a m b
feed iteratee enumerator =
    Iteratee $ do
        step <- runIteratee iteratee
        runIteratee $ enumerator step

注:feedは の特殊なケースです>>==

最初に、feed入力の準備ができるまで iteratee を実行します。次に、 iteratee の最初のStepものを列挙子に渡します。そこから列挙子が引き継ぎます。

この最後の文は非常に重要です。列挙子は iteratee を使って何でもできます。必要に応じて iteratee を完全に破棄できます。ただし、列挙子は通常、 iteratee に入力を提供し、制御を iteratee に戻します。

例 1: iteratee への列挙子のフィード

3 つの文字列を要求して出力する iteratee があるとします。

iter3 :: Iteratee String IO ()
iter3 = do
    lift $ putStrLn "Gimmie a string!"
    a <- head_
    lift $ putStrLn a
    lift $ putStrLn "Gimmie another string!"
    b <- head_
    lift $ putStrLn b
    lift $ putStrLn "Gimmie one more string!"
    c <- head_
    lift $ putStrLn c
    lift $ putStrLn "Thank you!"

head_Data.Enumerator.Listで定義されています。

iteratee に 1 つの文字列を供給する列挙子:

getString :: Enumerator String IO a
getString (Continue k) = do
    line <- lift getLine
    k (Chunks [line])
getString step = Iteratee $ return step

getString複数のアイテムを必要とする iteratee が指定された場合、 iteratee に最初のアイテムが供給されます。次に、getString それ自体が残りのアイテムを必要とします。

  • iter3返品する前に 3 つのアイテムが必要()です。

  • iter3 `feed` getString2つのアイテムが必要です。

  • iter3 `feed` getString `feed` getString1つのアイテムが必要です。

  • iter3 `feed` getString `feed` getString `feed` getStringこれ以上のアイテムは必要ありません。

  • iter3 `feed` getString `feed` getString `feed` getString `feed` getString上記と同等です。これはgetStringの 2 番目のケースで処理されます。

例 2: 入力を消費する列挙子

入力を消費する列挙子を考えてみましょう。

consumeEnum :: Enumerator String IO a
consumeEnum step = do
    lift $ putStrLn "I take without giving"
    _ <- head_
    Iteratee $ return step

何をしiter3 `feed` consumeEnumますか?consumeEnumこれは、の独自の実装を見ることで、ある程度答えることができます。最初にアイテムが必要で、それを破棄します。次に、トーチを に渡します。これにiter3は、さらに 3 つのアイテムが必要です。

feedしかし、コンビネータを振り返ってください。を実行することから始まりiter3、それを に渡しStepますconsumeEnum。これは"Gimmie a string!"、制御が に到達する前に表示されることを意味しますconsumeEnum

于 2011-10-22T06:31:40.617 に答える
1

列挙子がデータを消費することに問題はありません。これは iteratee トランスフォーマーであり、それ自体の入力を iteratee に送ることができます。列挙子を iteratee に適用する方法を見てください。列挙子に適用された iteratee に別の列挙子を適用することもできます。

于 2011-10-12T10:04:39.767 に答える