Подтвердить что ты не робот

Какая польза от остатков кабелепровода?

Я пытаюсь понять различия между каналами и трубами. В отличие от труб, кабелепровод имеет концепцию остатков. Для чего нужны остатки? Я хотел бы увидеть некоторые примеры, когда остатки необходимы.

А так как у труб нет понятия остатков, есть ли способ добиться с ними аналогичного поведения?

4b9b3361

Ответ 1

Габриэль указывает, что остатки всегда являются частью парсинга. Я не уверен, что соглашусь, но это может зависеть только от определения разбора.

Существует большая категория вариантов использования, которые требуют остатков. Синтаксический анализ, безусловно, один: в любой момент, когда синтаксический анализ требует какого-то взгляда, вам понадобятся остатки. Одним из примеров этого является функция getIndented в методе уценки, которая изолирует все предстоящие строки с определенным уровнем отступов, оставив остальные строки, которые будут обрабатываться позже.

Но гораздо более мирский набор примеров живет в самом кабеле. Каждый раз, когда вы имеете дело с упакованными данными (например, ByteString или Text), вам нужно прочитать кусок, проанализировать его каким-то образом, использовать оставшееся, чтобы отбросить лишнее, а затем сделать что-то с исходным контентом. Возможно, самым простым примером этого является dropWhile.

Фактически, я считаю, что оставшаяся проблема является такой основной базой функции потоковой библиотеки, что новый интерфейс 1.0 для кабелепровода даже не предоставляет пользователю возможность отключать остатки. Я знаю очень мало реальных случаев использования, которые не нуждаются в этом так или иначе.

Ответ 2

Я отвечу за pipes. Короткий ответ на ваш вопрос заключается в том, что предстоящая библиотека pipes-parse будет поддерживать остатки как часть более общей структуры синтаксического анализа. Я нахожу, что почти каждый случай, когда люди хотят получить остатки, на самом деле хочет синтаксический анализатор, поэтому я создаю проблему с остатками как подмножество синтаксического анализа. Вы можете найти текущий проект библиотеки здесь.

Однако, если вы хотите понять, как pipes-parse заставляет его работать, самым простым способом реализации остатков является просто использовать StateP для хранения буфера pushback. Для этого требуется только определение следующих двух функций:

import Control.Proxy
import Control.Proxy.Trans.State

draw :: (Monad m, Proxy p) => StateP [a] p () a b' b m a
draw = do
    s <- get
    case s of
        []   -> request ()
        a:as -> do
            put as
            return a

unDraw :: (Monad m, Proxy p) => a -> StateP [a] p () a b' b m ()
unDraw a = do
    as <- get
    put (a:as)

draw сначала обращается к буферу pushback, чтобы увидеть, есть ли какие-либо сохраненные элементы, выталкивая один элемент из стека, если он доступен. Если буфер пуст, вместо этого он запрашивает новый элемент из восходящего потока. Конечно, нет смысла иметь буфер, если мы не сможем оттолкнуть что-либо обратно, поэтому мы также определяем unDraw, чтобы вставить элемент в стек, чтобы сохранить его позже.

Изменить: Ой, я забыл включить полезный пример того, когда остатки полезны. Как говорит Майкл, takeWhile и dropWhile являются полезными случаями остатков. Здесь функция drawWhile (аналогичная тому, что Майкл называет takeWhile):

drawWhile :: (Monad m, Proxy p) => (a -> Bool) -> StateP [a] p () a b' b m [a]
drawWhile pred = go
  where
    go = do
        a <- draw
        if pred a
        then do
            as <- go
            return (a:as)
        else do
            unDraw a
            return []

Теперь представьте, что ваш продюсер:

producer () = do
    respond 1
    respond 3
    respond 4
    respond 6

... и вы подключили это к потребителю, который использовал:

consumer () = do
    evens <- drawWhile odd
    odds  <- drawWhile even

Если первый drawWhile odd не отодвинул последний элемент, который он нарисовал, вы бы сбросили 4, который не будет правильно передан во второй оператор drawWhile even.