Haskell管道 - 管道消耗它产生的东西(本身)

时间:2016-07-21 22:13:43

标签: haskell pipe haskell-pipes

我正在尝试使用Pipes编写一个webscraper,而且我已经参与了以下抓取的链接。我有一个process函数可以下载网址,查找链接并生成链接。

process :: Pipe Item Item (StateT CState IO) ()
 ....
    for (each links) yield
 ....

现在我想了解一些如何以递归方式跟随这些链接,将StateT穿过。我意识到可能会做一些更惯用的事情,然后使用单个管道来处理大量的刮刀(尤其是当我开始添加更多功能时),我可以提出建议。当我考虑多线程w /共享状态时,我可能不得不重新考虑设计。

2 个答案:

答案 0 :(得分:4)

您可以通过Pipe a b m r参数将m连接到副作用,该参数可以交换管道正在运行的Monad。您可以通过将管道的下游端连接到另一个管道来将链路重新排队,该管道将链路粘贴在队列中,并将管道的上游端连接到从队列中读取链接的管道。

我们的目标是写

import Pipes

loopLeft :: Monad m => Pipe (Either l a) (Either l b) m r -> Pipe a b m r

我们将使用一个管道,其下游输出Either l b要么是Left l要发回上游要么是Right b要发送到下游,要发送{{1回到上游输入l,它是来自上游的排队Either l aLeft l。我们将Right a连接在一起,制作一个只看到Left l来自上游的管道,只会产生a向下游。

在下游端,我们将bl推到堆栈上。我们Left l来自yield下游的r

Right r

在上游端,我们会在堆栈顶部寻找import Control.Monad import Control.Monad.Trans.State pushLeft :: Monad m => Pipe (Either l a) a (StateT [l] m) r pushLeft = forever $ do o <- await case o of Right a -> yield a Left l -> do stack <- lift get lift $ put (l : stack) 的内容。如果没有,我们会yield获取上游的值和await

yield

现在我们可以写popLeft :: Monad m => Pipe a (Either l a) (StateT [l] m) r popLeft = forever $ do stack <- lift get case stack of [] -> await >>= yield . Right (x : xs) -> do lift $ put xs yield (Left x) 了。我们将上游和下游管道与管道组成loopLeft组合在一起。 popLeft >-> hoist lift p >-> pushLefthoist lift变为Pipe a b m rdistributePipe a b (t m) r变为Pipe a b (t m) r。要返回到t (Pipe a b m) r,我们将以空堆栈Pipe a b m r开始运行整个StateT计算。在[]中,Pipes.LiftevalStateT的组合名称evalStateP很好。

distribute

答案 1 :(得分:3)

我会这样做:

import Pipes

type Url = String

getLinks :: Url -> IO [Url]
getLinks = undefined

crawl :: MonadIO m => Pipe Url Url m a
crawl = loop []
  where
    loop [] = do url <- await; loop [url]
    loop (url:urls) = do
      yield url
      urls' <- liftIO $ getLinks url
      loop (urls ++ urls')

您可以根据url'urls的合并方式实现DFS或BFS。