2013-03-15 14 views
13

Tôi đang cố gắng xóa một trang web bằng cách sử dụng Haskell và biên dịch các kết quả thành một đối tượng.Làm cách nào để tôi thực hiện trả lại khối sớm?

Nếu vì bất kỳ lý do gì, tôi không thể lấy tất cả các mục từ các trang, tôi muốn ngừng cố xử lý trang và quay lại sớm.

Ví dụ:

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

Vấn đề là when không chỉ dừng lại khối làm hoặc giữ cho các bộ phận khác được thực thi.

Cách phù hợp để thực hiện việc này là gì?

+1

Đây có phải là điều bạn muốn không? http://www.haskellforall.com/2012/07/breaking-from-loop.html –

Trả lời

14

return trong Haskell không làm điều tương tự như return bằng các ngôn ngữ khác. Thay vào đó, những gì return làm là để tiêm một giá trị vào một đơn nguyên (trong trường hợp này là IO). Bạn có một vài tùy chọn

đơn giản nhất là sử dụng nếu

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

tùy chọn khác là sử dụng 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() 

vấn đề chung ở đây là rằng IO đơn nguyên không có hiệu ứng kiểm soát (ngoại trừ trường hợp ngoại lệ). Mặt khác, bạn có thể sử dụng máy biến áp có lẽ đơn nguyên

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

nếu bạn thực sự muốn có được đầy đủ các hiệu ứng kiểm soát thổi bạn cần phải sử dụng 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() 

CẢNH BÁO: không ai trong số các mã trên đã được kiểm tra, hoặc thậm chí loại kiểm tra!

+0

Cách tiếp cận thứ hai phù hợp với tôi. Tôi nghĩ rằng bạn không làm gì với 'trừ khi (điều kiện) $ do' để biên dịch (thông báo' $ ') – kunigami

2

Tôi chưa bao giờ làm việc với Haskell, nhưng có vẻ như quitte dễ dàng. Hãy thử when (isNothing date) $ exit(). Nếu điều này cũng không hoạt động, hãy chắc chắn rằng câu lệnh của bạn là chính xác. Ngoài ra, hãy xem trang web này để biết thêm thông tin: Breaking From loop.

+4

Liên kết tốt, nhưng lưu ý rằng 'thoát' được định nghĩa trong ví dụ và không phải là nội trang dựng sẵn. Giải pháp trong bài đăng đó giống như giải pháp @ dave4420: một biến thể đơn nguyên. – luqui

12

Sử dụng máy biến áp đơn lẻ!

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

Đối với linh hoạt hơn trong các giá trị trả về khi bạn trở lại sớm, sử dụng throwError/eitherT/EitherT thay vì mzero/maybeT/MaybeT. (Mặc dù sau đó bạn không thể sử dụng guard.)

(Có lẽ cũng sử dụng headZ thay vì headMay và mương rõ ràng guard.)

+1

'Control.Error.Util' cần cho điều gì? –

+1

@Joehillen 'maybeT'. – dave4420

Các vấn đề liên quan