2012-08-07 18 views
21

Do tej pory użyłem zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r'), ale jest uważane za przestarzałe.Jaki jest preferowany sposób łączenia dwóch zlewów?

+0

Jakie zachowanie, * dokładnie *, czy chcesz mieć "połączone" pochłaniacze? Próbowałem spojrzeć na starą dokumentację i implementację 'zipSinks', ale zachowanie nie jest łatwo dostrzegalne na pierwszy rzut oka. –

+0

@DanBurton: 'zipSinks' pobiera dwa Zlewy i zwraca Zlew, który tworzy parę z wynikami odpowiednich Zlewów. Na przykład 'sizeCrc32Sink = zipSinks sizeSink crc32Sink' policzy rozmiar i sumę kontrolną. Mam to samo zachowanie, które opisał Oleg [tutaj] (http://okmij.org/ftp/Streams.html#1enum2iter). – tymmym

+0

Ok Widzę; w zasadzie uzależnia oczekiwania i zasila wyjściowe wyjście do obu zlewów jednocześnie, w rodzaju rozwidlenia strumienia wejściowego na dwa. Dokumenty dla Data.Conduit.Util stwierdzają, że "istnieją teraz łatwiejsze sposoby radzenia sobie z ich przypadkami użycia", ale nie widzę prostszego sposobu na to użycie, ponieważ wymaga to zagnieżdżenia się w wewnętrznych elementach kanału do implementacji. –

Odpowiedz

6

((Pakiet jest conduit-0.5.2.3. Cały module jest tylko dla wstecznej kompatybilności.))


[edit]

Więc moje proste monadycznego przypuszczenie (patrz niżej) wydaje się być zła, mimo że typy są poprawne. Teraz mogę tylko zgadywać, że odpowiedź brzmi:

Funkcje wymiany są nadal w fazie rozwoju, podobnie jak wszystkie rury i kanały kablowe oraz podobne koncepcje i biblioteki.

Będę czekać na następny interfejs API, aby rozwiązać to pytanie i nadal używać do tego czasu zipSink. (może to była tylko niesłuszna.)

[/edit]

nie jestem, że znają z tego pakietu, ale nie byłoby zrobić tak samo jak ten?

zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') 
zipSinks s1 s2 = (,) <$> s1 <*> s2 

W końcu jest to Monad. (Functor, Applicative)

zipSinks :: Monad sink => sink r -> sink r' -> sink (r, r') 
zipSinks s1 s2 = liftM2 (,) s1 s2 
+2

Typy są w porządku, ale nie semantyki. Twoja wersja 'zipSinks' będzie uruchamiać zlewy kolejno, a pierwsze zlew zostanie w pełni zużyte. – tymmym

7

Edit

Po uwzględnieniu tego, ja nie sądzę, że jest to możliwe przy obecnej wersji Data.Conduit. Rury nie są kategoriami, więc &&& jest wykluczone. I nie ma sposobu, abym mógł wymyślić, by wyciągnąć wyniki z prądu, podawać je stopniowo do obu zlewów i zwierać, gdy kończy się pierwszy zlew. (Chociaż nie sądzę, że zwarcie w ten sposób działa, wydaje się, że byłoby to bardzo pożądane.) Z wyjątkiem oczywiście dopasowania do wzorca na obu zlewach (jak w pakiecie ma zipSinks), ale to właśnie jesteśmy próbując tego uniknąć.

To powiedziawszy, chciałbym kochać, aby udowodnić, że jest źle tutaj.

To nie jest ładne, ale możesz to zrobić w sposób oczywisty.

Pierwsze import:

module Main where 

import Control.Monad.Trans 
import Data.Conduit 
import qualified Data.Conduit.Binary as CB 
import qualified Data.Conduit.List as CL 
import qualified Data.Conduit.Text as CT 
import qualified Data.Conduit.Util as CU 
import Data.Maybe 
import Data.Text (unpack) 

Teraz zipSinks. Zasadniczo chcesz utworzyć zlew, który pobiera dane wejściowe z kanału i przesyła je osobno do każdego zlewu podrzędnego. W tym przypadku użyłem do tego celu CL.sourceList. Jeśli await zwraca Nothing, maybeToList zwraca pustą listę, więc pochłaniacze podrzędne są również uruchamiane bez wprowadzania danych. Na koniec wynik każdego zlewu podrzędnego jest podawany do krotki.

zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') 
zipSinks s1 s2 = do 
    l <- fmap maybeToList await 
    o1 <- lift $ CL.sourceList l $$ s1 
    o2 <- lift $ CL.sourceList l $$ s2 
    return (o1, o2) 

Oto kilka przykładów użycia zipSinks. Wygląda na to, że działa dobrze zarówno wewnątrz modelu IO, jak i poza nim, a w kilku testach, które zrobiłem, wyjście dopasowuje wyjście zipped', utworzone przy użyciu starego zipSinks.

doubleHead :: Monad m => Sink Int m (Maybe Int) 
doubleHead = await >>= return . fmap (2*) 

-- old version 
zipped' :: Monad m => Sink Int m (Maybe Int, Maybe Int) 
zipped' = CU.zipSinks CL.head doubleHead 

-- new version 
zipped :: Monad m => Sink Int m (Maybe Int, Maybe Int) 
zipped = zipSinks CL.head doubleHead 

fromList = CL.sourceList [7, 8, 9] $$ zipped 
-- (Just 7, Just 14) 

fromFile :: String -> IO (Maybe Int, Maybe Int) 
fromFile filename = runResourceT $ 
     CB.sourceFile filename 
    $= CB.lines 
    $= CT.decode CT.utf8 
    $= CL.map (read . unpack) 
    $$ zipped 

-- for a file with the lines: 
-- 
-- 1 
-- 2 
-- 3 
-- 
-- returns (Just 1, Just 2) 
+0

Nice! (NB, możesz napisać 'oczekuj >> = return .fmap (2 *)' dla 'doubleHead', i podobnie,' l <- fmap maybeToList czekaj' zamiast używać 'input' w' zipSinks' .Ale jest, '' Data.Conduit.Internals "jest obcym importem?) – huon

+0

Tak, zdałem sobie sprawę, że prawdopodobnie mogłem użyć funktorów w niektórych miejscach. W Haskell wciąż mam dość n00b, co niestety jest dla mnie zwykle edytorem drugiego przejścia. I tak, 'Data.Conduits.Internals' jest nieistotny. Początkowo szukałem przy użyciu 'sinkToPipe' z niego. Dzięki za wskazanie tych. Zaktualizuję odpowiedź. – Eric

+2

Twoja wersja 'zipSinks' łączy tylko pierwsze elementy. Na przykład 'runResourceT $ CL.sourceList [1,2,3] $$ zipSinks (CL.take 2) (CL.take 2)' zwróci '([1], [1])', ale powinno '([1 , 2], [1,2]) ". – tymmym

Powiązane problemy