2016-07-21 14 views
3

私はパイプを使ってウェブスクレイパーを書こうとしていますが、私は次のスクラップされたリンクの部分に来ました。私はprocess URLをダウンロードし、リンクを見つけてそれらを生成する関数を持っています。Haskell Pipes - パイプの消費量(それ自体)

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

ここで、これらのリンクをどのように再帰的にフォローして、StateTをスルーしたいのですか。私はおそらく、より多くの慣用的な作業をしていることを理解しています(特に私がより多くの機能を追加するようになったとき)。とにかく私が共有状態でマルチスレッディングを検討するとき、私はおそらくデザインを考え直さなければならないでしょう。

+1

私はおそらく 'process ::(MonadState CState m、MonadIO m)=>パイプ項目項目m()'を持っています。あまりコードが変更されず(おそらく)、読みやすくなり、モナドスタックの実装の詳細が抽象化されます。 – Alec

答えて

4

mパラメータを使用してPipe a b m rを接続すると、パイプが動作しているMonadがスワップアウトされます。これを使用して、パイプの下流側をキューにリンクを張っている別のパイプに接続し、パイプの上流側をキューからリンクを読み取るパイプに接続することで、リンクを再キューに入れることができます。

私たちの目標は、私たちは、その下流の出力、Either l b、下流送信するために上流返信するLeft lか​​のいずれかであるパイプを取り、後ろにl Sをお送りします

import Pipes 

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

を書くことですアップストリーム入力Either l aは、アップストリームから来るLeft lまたはRight aのキューに入っています。 Left lを一緒に接続して、上流から来るのはaしか見えないパイプを作り、下流にはbがあります。

下流側でLeft lからlをスタックにプッシュします。我々は、yieldrRight 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 >-> pushLeftと一緒にアップストリームとダウンストリームのパイプを構成します。 hoist liftPipe a b m rPipe a b (t m) rに変えます。 distributeは、Pipe a b (t m) rt (Pipe a b m) rに変えます。 Pipe a b m rに戻るには、空のスタック[]で始まるStateTの計算全体を実行します。 Pipes.Liftには、evalStateTdistributeの組み合わせのためのいい名前evalStatePがあります。

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 
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') 

あなたはurlsurl'を組み合わせた方法に応じて、DFSやBFSを達成することができます。

関連する問題