[英]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
就像conduit
的await
:它從剩余的緩沖區(即StateP
部分)或上游(如果緩沖區為空)請求一個值。 Nothing
表示文件結束。
您可以使用pipes-parse
類型為wrap
pipes-parse
的wrap
功能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.