我正在尝试使用 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 块或阻止其他部分执行。
这样做的正确方法是什么?
return
与其他语言中的 return
的功能不同。 相反,return
所做的是将一个值注入到 monad 中(在本例中为 IO
)。 你有几个选择
最简单的是使用if
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
monad 没有控制效果(例外情况除外)。 另一方面,你可以使用也许 monad 转换器
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 = evalContT $ callCC $ \earlyReturn -> do
doc <- fromUrl url
title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText
when (isNothing title) $ earlyReturn ()
date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc"
when (isNothing date) $ earlyReturn ()
-- etc
-- make page object and send it to db
return ()
警告:以上代码均未经过测试,甚至未进行类型检查!
使用 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
。)
我从未使用过 Haskell,但这看起来相当简单。尝试
when (isNothing date) $ exit ()
。如果这也不起作用,那么请确保您的陈述是正确的。另请参阅此网站了解更多信息:打破循环。