如何让 do 块提前返回?

问题描述 投票:0回答:3

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

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

haskell web-scraping monads
3个回答
19
投票
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 没有控制效果(例外情况除外)。 另一方面,你可以使用也许 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 ()

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


13
投票

使用 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
投票

我从未使用过 Haskell,但这看起来相当简单。尝试

when (isNothing date) $ exit ()
。如果这也不起作用,那么请确保您的陈述是正确的。另请参阅此网站了解更多信息:打破循环

© www.soinside.com 2019 - 2024. All rights reserved.