2012-08-07 52 views
21

Tôi đã sử dụng zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') cho điều này nhưng bị coi là không dùng nữa.Cách ưa thích để kết hợp hai bồn rửa là gì?

+0

Hành vi nào, * chính xác *, bạn có muốn các bồn "kết hợp" có? Tôi đã thử xem tài liệu cũ và thực hiện 'zipSinks', nhưng hành vi này không dễ nhận thấy trong nháy mắt. –

+0

@DanBurton: 'zipSinks' mất hai Sinks và trả về một Sink tạo ra một cặp với kết quả của Sinks tương ứng. Ví dụ 'sizeCrc32Sink = zipSinks sizeSink crc32Sink' sẽ đếm kích thước và tổng kiểm tra. Tôi hành vi tương tự như được mô tả bởi Oleg [ở đây] (http://okmij.org/ftp/Streams.html#1enum2iter). – tymmym

+0

Ok tôi thấy; nó về cơ bản móc lên những chờ đợi và nguồn cấp dữ liệu đầu ra thượng nguồn cho cả hai bồn cùng một lúc, loại forking dòng đầu vào trong hai. Các tài liệu cho Data.Conduit.Util nói rằng "có những cách dễ dàng hơn để xử lý các trường hợp sử dụng của chúng" nhưng tôi thấy không có cách nào dễ dàng hơn cho trường hợp sử dụng này, vì nó yêu cầu delving vào ruột ruột để thực hiện. –

Trả lời

6

((Các gói phần mềm là conduit-0.5.2.3. Toàn bộ module chỉ là để tương thích ngược.))


[chỉnh sửa]

Vì vậy, đoán monadic đơn giản của tôi (xem dưới đây) dường như là sai, mặc dù các loại là chính xác. Bây giờ, tôi chỉ có thể đoán rằng câu trả lời là:

Các tính năng thay thế vẫn đang được phát triển, khá nhiều giống như tất cả ống/Conduit và các khái niệm và các thư viện tương tự.

Tôi sẽ đợi API tiếp theo giải quyết câu hỏi này và vẫn sử dụng zipSink cho đến lúc đó. (Có lẽ nó đã được chỉ đặt không đúng chỗ.)

[/chỉnh sửa]

tôi không phải là quen với gói này, nhưng nó sẽ không làm chỉ giống như này?

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

Sau cùng, tất cả là Monad. (Functor, Applicative)

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

Các loại là ok, nhưng không phải là ngữ nghĩa. Phiên bản 'zipSinks' của bạn sẽ chạy chìm liên tiếp và bồn rửa đầu tiên sẽ tiêu thụ hoàn toàn nguồn. – tymmym

7

Sửa

Sau khi xem xét này, tôi không nghĩ rằng nó có thể với phiên bản hiện tại của Data.Conduit. Ống không phải là Danh mục, do đó, &&& không nằm trong câu hỏi. Và không có cách nào mà tôi có thể nghĩ đến để kéo kết quả từ thượng nguồn, cho chúng tăng dần lên cả hai bồn rửa và ngắn mạch khi bồn rửa đầu tiên kết thúc. (Mặc dù tôi không nghĩ rằng ngắn mạch theo cách này, nó có vẻ như nó sẽ rất hấp dẫn.) Ngoại trừ tất nhiên, để phù hợp với mô hình trên cả hai Sinks (như zipSinks trong gói), nhưng đó là những gì chúng tôi cố gắng tránh ở đây.

Điều đó nói rằng, tôi sẽ yêu thích để được chứng minh là sai ở đây.


Nó không đẹp, nhưng bạn có thể làm điều này một cách rõ ràng.

nhập khẩu đầu tiên:

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) 

Bây giờ cho zipSinks. Về cơ bản, bạn muốn tạo một bồn rửa kéo đầu vào từ phía trên và gửi nó đến từng bồn rửa riêng biệt. Trong trường hợp này, tôi đã sử dụng CL.sourceList để thực hiện việc này. Nếu await trả về Nothing, maybeToList trả về một danh sách trống, vì vậy các bồn rửa trẻ em cũng được chạy mà không có đầu vào. Cuối cùng, đầu ra của mỗi bồn rửa trẻ em sau đó được đưa vào tuple.

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) 

Dưới đây là một số ví dụ về cách sử dụng zipSinks. Nó xuất hiện để làm việc tốt cả bên trong của IO và bên ngoài của nó, và trong vài bài kiểm tra tôi đã làm, đầu ra phù hợp với đầu ra của zipped', tạo ra bằng cách sử dụng cũ 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

Tuyệt vời! (NB, bạn có thể viết 'await >> = return. Fmap (2 *)' cho 'doubleHead', và tương tự,' l <- fmap maybeToList chờ đợi thay vì sử dụng 'input' trong' zipSinks'. Data.Conduit.Internals' một nhập khẩu không liên quan?) – huon

+0

Vâng, tôi nhận ra rằng tôi có lẽ có thể đã sử dụng functors ở một số nơi. Tôi vẫn còn đủ một n00b tại Haskell rằng đó là một bản chỉnh sửa thứ hai cho tôi, thật không may. Và có, 'Data.Conduits.Internals' là không liên quan. Ban đầu, tôi đã xem xét sử dụng 'sinkToPipe' từ nó. Cảm ơn bạn đã chỉ ra những điều này. Tôi sẽ cập nhật câu trả lời. – Eric

+2

Phiên bản 'zipSinks' của bạn chỉ kết hợp các yếu tố đầu tiên. Ví dụ 'runResourceT $ CL.sourceList [1,2,3] $$ zipSinks (CL.take 2) (CL.take 2)' sẽ trả về '([1], [1])' nhưng nên '([1] , 2], [1,2]) '. – tymmym

Các vấn đề liên quan