18

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

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

4

3 回答 3

18

return在 haskell 中做的事情与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 ()

这里的一般问题是IOmonad 没有控制效果(例外情况除外)。另一方面,您可以使用可能的单子变压器

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

警告:以上代码都没有经过测试,甚至没有类型检查!

于 2013-03-15T21:15:25.513 回答
13

使用单子变压器!

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

于 2013-03-15T21:14:29.640 回答
1

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

于 2013-03-15T20:59:21.220 回答