Ответ 1
Да, перечислитель может использовать данные. Перечислитель в основном принимает итерацию и преобразует ее в одну итерацию после того, как ее накормили некоторые элементы. Если перечислитель запрашивает ввод, то полученный итератор будет запрашивать ввод.
Как перечислитель пересылается в 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
запускает итерацию, пока она не будет готова для ввода. Затем он передает итератору первый Step
в счетчик. Перечислитель переходит оттуда.
Последнее предложение очень важно. Перечислитель может делать все, что захочет, с его итерацией. Он может полностью отказаться от итерации, если захочет. Тем не менее, перечислитель обычно передает итератору свой вход, а затем передает его обратно в итерацию.
Пример 1: Подача счетчиков на итерацию
Предположим, что у нас есть итерация, которая запрашивает три строки и печатает их:
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. Список.суб >
и перечислитель, который передает его итерацию одной строкой:
getString :: Enumerator String IO a
getString (Continue k) = do
line <- lift getLine
k (Chunks [line])
getString step = Iteratee $ return step
Когда getString
задается итерация, которая требует более одного элемента, она будет кормить итерацию первым элементом. Тогда самому getString
понадобятся остальные элементы.
-
iter3
требуется три элемента, прежде чем он сможет вернуть()
. -
iter3 `feed` getString
требуется два элемента. -
iter3 `feed` getString `feed` getString
нужен один элемент. -
iter3 `feed` getString `feed` getString `feed` getString
больше не требуется элементов. -
iter3 `feed` getString `feed` getString `feed` getString `feed` getString
эквивалентно приведенному выше. Это обрабатывается вторым случаемgetString
.
Пример 2: Перечислитель, который потребляет входные данные
Рассмотрим перечислитель, который потребляет вход:
consumeEnum :: Enumerator String IO a
consumeEnum step = do
lift $ putStrLn "I take without giving"
_ <- head_
Iteratee $ return step
Что делает iter3 `feed` consumeEnum
? На это можно ответить, посмотрев на собственную реализацию consumeEnum
. Сначала ему нужен элемент и отбрасывает его. Затем он передает факел на iter3
, для которого требуется еще три элемента.
Однако вернемся к комбинатору feed
. Он начинается с запуска iter3
, затем передает Step
значение consumeEnum
. Это означает, что "Gimmie a string!"
будет напечатано до того, как управление достигнет consumeEnum
.