Odpowiem za pipes
. Krótka odpowiedź na twoje pytanie jest taka, że nadchodząca biblioteka pipes-parse
będzie obsługiwać resztki w ramach bardziej ogólnego schematu analizowania. Uważam, że prawie każdy przypadek, w którym ludzie chcą resztek, które faktycznie chcą parsera, dlatego ustawiam problem z resztkami jako podzbiór parsowania. Możesz znaleźć aktualny projekt biblioteki here.
Jednakże, jeśli chcesz zrozumieć, w jaki sposób pipes-parse
uruchamia to, najprostszym sposobem na wdrożenie resztek jest użycie StateP
do przechowywania bufora wstecznego. To wymaga zdefiniowania tylko dwie następujące funkcje:
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
najpierw konsultuje bufor Pushback, aby zobaczyć, czy są jakieś przechowywane elementy, popping jeden element ze stosu jeśli są dostępne. Jeśli bufor jest pusty, zamiast tego żąda nowego elementu od źródła. Oczywiście, nie ma sensu posiadanie bufora, jeśli nie możemy niczego odepchnąć, więc definiujemy również unDraw
, aby wypchnąć element na stos, aby zapisać go na później.
Edytuj: Ups, zapomniałem podać użyteczny przykład, kiedy resztki są przydatne. Tak jak Michael mówi, takeWhile
i dropWhile
są przydatnymi przypadkami resztek. Oto funkcja drawWhile
(analogicznie do tego, co Michael nazywa 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 []
Teraz wyobraź sobie, że producent był:
producer() = do
respond 1
respond 3
respond 4
respond 6
... i że związał się z konsumentem, które kiedyś:
consumer() = do
evens <- drawWhile odd
odds <- drawWhile even
Jeśli pierwszy nie odepchnie ostatniego elementu, który narysował, to zrzucisz 4
, który nie będzie poprawnie przekazane do drugiego oświadczenia drawWhile even
".
Dzięki za wyjaśnienie, jestem już całkiem przekonany. Tymczasem zastanawiałem się, jak zaimplementować pozostałości po bibliotece podobnej do przewodnika, która nie ma ich natywnie. Chodzi o to, że przewód z resztkami może być reprezentowany jako kanał zwracający '(Maybe i, r)'. Moją próbą (dla przewodnika) jest [tutaj] (https://gist.github.com/ppetr/5110909). –
Myślę, że masz odpowiednią intuicję, twoja implementacja jest bardzo podobna do tego, jak działa wewnętrznie. Wydaje mi się, że odkryłeś kwestię podwójnego pozostawienia, dlatego resztki mogą być układane jako wiele pozostawionych konstruktorów w przewodzie. –
Jeszcze [kolejna próba] (https://gist.github.com/ppetr/5110909#file-feedback-hs) (której najprawdopodobniej użyję w mojej bibliotece Scala) dla resztek to zobaczenie resztek jako pewnego rodzaju feedback: Dla 'Pipe Void i (Either io) umr' wysyłamy dowolne' Left i' z powrotem do jego wejścia za pomocą wewnętrznej metody, która przekształca taką rurę w standardową. –