2013-03-15 12 views
13

Próbuję zeskrobać stronę internetową za pomocą Haskella i skompilować wyniki w obiekcie.Jak wcześnie dokonać zwrotu bloku?

Jeśli z jakiegoś powodu nie mogę pobrać wszystkich elementów ze stron, chcę przerwać próbę przetworzenia strony i powrotu wcześniej.

Na przykład:

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

Problemem jest when nie zatrzymuje blok zrobić lub zachować inne części przed egzekucją.

Co to jest właściwy sposób?

+1

Czy tego chcesz? http://www.haskellforall.com/2012/07/breaking-from-loop.html –

Odpowiedz

14

return w Haskell nie to samo jak return w innych językach. Zamiast tego, co robi return jest wstrzyknięcie wartości do monady (w tym przypadku IO). Masz kilka opcji

najprostsze jest wykorzystanie jeśli

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

Inną opcją jest użycie 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() 

ogólny problem polega na tym, że IO monada nie ma efekty kontrolne (z wyjątkiem wyjątków). Z drugiej strony, można użyć może monada transformator

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

jeśli naprawdę chcesz dostać pełnowartościowy efekty kontroli musisz użyć 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() 

Ostrzeżenie: żaden z wyżej kod został testowane, a nawet sprawdzane!

+0

Drugie podejście sprawdziło się dobrze. Myślę, że możesz zrobić to do 'chyba że (warunek) $ do' do kompilacji (zwróć uwagę na '$') – kunigami

2

Nigdy nie pracowałem z Haskellem, ale wydaje mi się to proste. Wypróbuj when (isNothing date) $ exit(). Jeśli to również nie działa, upewnij się, że twoje oświadczenie jest poprawne. Zobacz także tę stronę internetową, aby uzyskać więcej informacji: Breaking From loop.

+4

Dobry link, ale zauważ, że 'exit' jest zdefiniowany w przykładzie i nie jest wbudowany. Rozwiązanie w tym poście jest takie samo jak rozwiązanie @ dave4420: transformator monadowy. – luqui

12

Użyj transformatora Monada!

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

Dla większej elastyczności w wartości zwracanej podczas wcześniejszego powrotu, należy throwError/eitherT/EitherT zamiast mzero/maybeT/MaybeT. (Chociaż to nie można używać guard.)

(Prawdopodobnie również użyć headZ zamiast headMay i rów wyraźny guard.)

+1

Do czego służy "Control.Error.Util"? –

+1

@Joehillen 'maybeT'. – dave4420