我如何提早做回块?

时间:2013-03-15 20:56:22

标签: haskell web-scraping monads

我正在尝试使用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不会停止执行阻止或阻止执行其他部分。

这样做的正确方法是什么?

3 个答案:

答案 0 :(得分:16)

haskell中的

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没有控制效果(例外情况除外)。另一方面,你可以使用may 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 = 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 ()

警告:以上代码均未经过测试,甚至未经过类型检查!

答案 1 :(得分: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 / {{3 }}。 (虽然那时你不能使用MaybeT。)

(也可能使用guard代替headMay而忽略显式guard。)

答案 2 :(得分:2)

我从未使用过Haskell,但似乎很容易戒掉。试试when (isNothing date) $ exit ()。如果这也不起作用,那么请确保您的陈述是正确的。另请参阅此网站了解更多信息:Breaking From loop