2013-03-15 31 views
13

我試圖用Haskell來抓取網頁並將結果編譯成對象。我該如何儘早做一個塊迴歸?

如果出於任何原因,我無法從頁面中獲取所有項目,我想停止嘗試處理頁面並提前返回。

例如:

scrapePage :: String -> IO() 
scrapePage url = do 
    doc <- fromUrl url 
    title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText 
    when (isNothing title) (return()) 
    date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc" 
    when (isNothing date) (return()) 
    -- etc 
    -- make page object and send it to db 
    return() 

問題是when不會停止DO塊或保持其他部分被執行。

這樣做的正確方法是什麼?

+1

這是你想要的嗎? http://www.haskellforall.com/2012/07/breaking-from-loop.html – 2013-03-15 22:12:39

回答

14

在Haskell沒有做同樣的事情在其他語言returnreturn。相反,return所做的是將值注入monad(在這種情況下爲IO)。你有幾個選項

最簡單的是,如果

scrapePage :: String -> IO() 
scrapePage url = do 
    doc <- fromUrl url 
    title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText 
    if (isNothing title) then return() else do 
    date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc" 
    if (isNothing date) then return() else do 
    -- etc 
    -- make page object and send it to db 
    return() 

另一種選擇是使用unless

scrapePage url = do 
    doc <- fromUrl url 
    title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText 
    unless (isNothing title) do 
    date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc" 
    unless (isNothing date) do 
     -- etc 
     -- make page object and send it to db 
     return() 

普遍的問題是這裏的IO單子沒有使用控制效果(除了例外)。在另一方面,你可以使用也許單子轉換

scrapePage url = liftM (maybe() id) . runMaybeT $ do 
    doc <- liftIO $ fromUrl url 
    title <- liftIO $ liftM headMay $ runX $ doc >>> css "head.title" >>> getText 
    guard (isJust title) 
    date <- liftIO $ liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc" 
    guard (isJust date) 
    -- etc 
    -- make page object and send it to db 
    return() 

,如果你真的想要得到你需要使用ContT

scrapePage :: String -> IO() 
scrapePage url = runContT return $ do 
    doc <- fromUrl url 
    title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText 
    when (isNothing title) $ callCC ($()) 
    date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc" 
    when (isNothing date) $ callCC ($()) 
    -- etc 
    -- make page object and send it to db 
    return() 

警告完全成熟的控制效果:無上面的代碼已經測試,甚至類型檢查!

+0

第二種方法適用於我。我認爲你應該去 '除非(條件)$ do' 它要編譯(注意'$') – kunigami 2015-11-27 01:34:24

2

我從來沒有與Haskell合作過,但似乎很容易。嘗試when (isNothing date) $ exit()。如果這也不起作用,那麼確保你的陳述是正確的。有關詳情,請參閱此網站:Breaking From loop

+4

很好的鏈接,但請注意''exit'在示例中定義,不是內置的。該帖子中的解決方案與@ dave4420的解決方案相同:一個monad變壓器。 – luqui 2013-03-15 21:42:44

12

使用monad變壓器!

import Control.Monad.Trans.Class -- from transformers package 
import Control.Error.Util  -- from errors package 

scrapePage :: String -> IO() 
scrapePage url = maybeT (return()) return $ do 
    doc <- lift $ fromUrl url 
    title <- liftM headMay $ lift . runX $ doc >>> css "head.title" >>> getText 
    guard . not $ isNothing title 
    date <- liftM headMay $ lift . runX $ doc >>> css "span.dateTime" ! "data-utc" 
    guard . not $ isNothing date 
    -- etc 
    -- make page object and send it to db 
    return() 

對於返回值更大的靈活性,當你早日歸來,使用throwError/eitherT/EitherT,而不是mzero/maybeT/MaybeT。 (雖然那麼你可以不使用guard

(可能也使用headZ代替headMay和溝明確guard。)

+1

Control.Error.Util需要什麼? – 2013-03-15 21:39:14

+1

@Joehillen'maybeT'。 – dave4420 2013-03-15 21:43:33