Haskell管道——让管道消耗它产生的(自身)
我正在尝试使用管道编写一个webscraper,我已经来到了下面的链接部分。我有一个Haskell管道——让管道消耗它产生的(自身),haskell,pipe,haskell-pipes,Haskell,Pipe,Haskell Pipes,我正在尝试使用管道编写一个webscraper,我已经来到了下面的链接部分。我有一个过程函数,可以下载url、查找链接并生成它们 process :: Pipe Item Item (StateT CState IO) () .... for (each links) yield .... 现在我想了解一下如何递归地跟踪这些链接,并将StateT贯穿其中。我意识到有可能会做一些更为惯用的事情,然后在大部分刮板上使用单个管道(特别是当我开始添加更多功能时),我愿意听取建议。当我考虑多
过程
函数,可以下载url、查找链接并生成它们
process :: Pipe Item Item (StateT CState IO) ()
....
for (each links) yield
....
现在我想了解一下如何递归地跟踪这些链接,并将StateT贯穿其中。我意识到有可能会做一些更为惯用的事情,然后在大部分刮板上使用单个管道(特别是当我开始添加更多功能时),我愿意听取建议。当我考虑多线程W/Stand状态时,我可能不得不重新考虑设计。 < P>我会这样做:
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=String
getLinks::Url->IO[Url]
getLinks=未定义
爬网::MonadIO m=>管道Url m a
爬网=循环[]
哪里
loop[]=do url您可以通过m
参数将管道a b m r
连接到副作用,该参数将管道运行的Monad
替换掉。通过将管道的下游端连接到将链接粘贴到队列中的另一个管道,并将管道的上游端连接到从队列中读取链接的管道,可以使用此选项对链接进行重新排队
我们的目标是写作
import Pipes
loopLeft :: Monad m => Pipe (Either l a) (Either l b) m r -> Pipe a b m r
我们将取一个管道,其下游输出,或Lb
,要么是一个左l
向上游发送,要么是一个右b
向下游发送,然后将l
返回上游输入或La
,这要么是一个排队的左l
,要么是来自上游的右a
。我们将把左l
s连接在一起,形成一个管道,只看到a
s来自上游,只产生b
s流向下游
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)
在下游端,我们将把l
s从Left l
推到堆栈上。我们从右r
下游生产r
s
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)
现在我们可以编写loopLeft
。我们将上游和下游管道与管道组合一起构成popLeft>->提升提升p>->pushLeft
。提升装置将管道a b m r
转换为管道a b(t m)r
。将a管道a b(TM)r
转换为at(管道a b m)r
。为了回到管道a b m r
,我们从空堆栈开始运行整个StateT
计算[]
。在Pipes.Lift
中,evalStateT
和distribute
的组合有一个很好的名字
import Pipes.Lift
loopLeft :: Monad m => Pipe (Either l a) (Either l b) m r -> Pipe a b m r
loopLeft p = flip evalStateT [] . distribute $ popLeft >-> hoist lift p >-> pushLeft
偏好问题,但我可能有流程::(MonadState CState m,MonadIO m)=>管道项目m()
。没有太多的代码更改(可能),可读性更高,并且抽象了monad堆栈的实现细节。