簡體   English   中英

我如何編寫一個向下游發送從上游接收到的列表的管道?

[英]How can I write a pipe that sends downstream a list of what it receives from upstream?

我很難用此簽名編寫pipe

toOneBigList :: (Monad m, Proxy p) => () -> Pipe p a [a] m r

它應該簡單地采取一切a從上游S和下游的清單給他們。

我所有的嘗試看起來都從根本上打破了。

有人能指出我正確的方向嗎?

有兩種基於pipes的解決方案,我將讓您選擇您更喜歡的一種。

注意:不清楚為什么要在下游接口上輸出列表,而不是直接將其返回。

導管式

第一個非常接近基於conduit的解決方案,它使用即將到來的pipes-pase ,它基本上是完整的,只需要文檔即可。 您可以在Github上找到最新的草稿

使用pipes-parse ,解決方案與Petr提供的conduit解決方案相同:

import Control.Proxy
import Control.Proxy.Parse

combine
    :: (Monad m, Proxy p)
    => () -> Pipe (StateP [Maybe a] p) (Maybe a) [a] m ()
combine () = loop []
  where
    loop as = do
        ma <- draw
        case ma of
            Nothing -> respond (reverse as)
            Just a  -> loop (a:as)

draw就像conduitawait :它從剩余的緩沖區(即StateP部分)或上游(如果緩沖區為空)請求一個值。 Nothing表示文件結束。

您可以使用pipes-parse類型為wrap pipes-parsewrap功能wrap沒有文件結尾的pipes-parse

wrap :: (Monad m, Proxy p) => p a' a b' b m r -> p a' a b' (Maybe b) m s

經典煙斗風格

第二種選擇更簡單。 如果要折疊給定的管道,可以直接使用WriterP進行WriterP

import Control.Proxy
import Control.Proxy.Trans.Writer

foldIt
  :: (Monad m, Proxy p) =>
     (() -> Pipe p a b m ()) -> () -> Pipe p a [b] m ()
foldIt p () = runIdentityP $ do
    r <- execWriterK (liftP . p >-> toListD >-> unitU) ()
    respond r

這是對所發生情況的更高級描述,但是它需要將管道作為顯式參數傳遞。 由您決定選擇哪一個。

順便說一句,這就是為什么我問您為什么要向下游發送單個值的原因。 如果返回折疊列表,則上面的代碼要簡單得多:

foldIt p = execWriterK (liftP . p >-> toListD)

如果p在其代理類型中完全是多態的,則甚至甚至不需要liftP 我僅將其作為預防措施。

獎金解決方案

toOneBigList pipes-parse不提供toOneBigList是,它始終是將結果分組到列表中的管道反模式。 pipes具有幾個不錯的功能,即使您試圖產生多個列表,也可以永遠不必將輸入分組到一個列表中。 例如,使用respond組合,您可以使代理產生將要遍歷的流的子集,然后注入使用該子集的處理程序:

example :: (Monad m, Proxy p) => () -> Pipe p a (() -> Pipe p a a m ()) m r
example () = runIdentityP $ forever $ do
    respond $ \() -> runIdentityP $ replicateM_ 3 $ request () >>= respond

printIt :: (Proxy p, Show a) => () -> Pipe p a a IO r
printIt () = runIdentityP $ do
    lift $ putStrLn "Here we go!"
    printD ()

useIt :: (Proxy p, Show a) => () -> Pipe p a a IO r
useIt = example />/ (\p -> (p >-> printIt) ())

以下是使用方法的示例:

>>> runProxy $ enumFromToS 1 10 >-> useIt
Here we go!
1
2
3
Here we go!
4
5
6
Here we go!
7
8
9
Here we go!
10

這意味着即使您需要對元素進行分組,也無需將單個元素帶入內存。

我只會給出部分答案,也許其他人會有更好的答案。

據我所知,標准管道沒有檢測管道另一部分何時終止的機制。 第一個終止的管道將產生管道的最終結果,而所有其他管道都將被丟棄。 因此,如果您有一個永遠消耗輸入(最終產生一個列表)的管道,那么在上游結束時它將沒有機會采取行動並產生輸出。 (這是有意的,因此上游和下游部分都相互對偶。)也許這可以在管道頂部的某些庫中解決。

情況與導管不同。 它具有消耗功能,該功能將所有輸入組合到一個列表中並返回(而不是輸出)它。 編寫需要的函數,最后輸出列表,這並不困難:

import Data.Conduit

combine :: (Monad m) => Conduit a m [a]
combine = loop []
  where
    loop xs = await >>= maybe (yield $ reverse xs) (loop . (: xs))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM