2013-03-15 10 views
13

Sto provando a cercare una pagina web usando Haskell e compile i risultati in un oggetto.Come posso fare in modo che un blocco di blocco venga restituito in anticipo?

Se, per qualsiasi motivo, non riesco a ottenere tutti gli elementi dalle pagine, voglio smettere di cercare di elaborare la pagina e tornare presto.

Ad esempio:

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

Il problema è il when non interrompe il blocco do o mantenere le altre parti vengano eseguiti.

Qual è il modo giusto per farlo?

+1

È questo quello che vuoi? http://www.haskellforall.com/2012/07/breaking-from-loop.html –

risposta

14

return in Haskell non fare la stessa cosa come return in altre lingue. Invece, ciò che fa return consiste nell'iniettare un valore in una monade (in questo caso IO). Hai un paio di opzioni

il più semplice è quello di utilizzare se

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

un'altra opzione è quella di utilizzare 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() 

il problema generale qui è che la monade IO non ha effetti di controllo (tranne eccezioni). D'altra parte, si potrebbe usare il trasformatore forse monade

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

se si vuole davvero ottenere effetti in piena regola di controllo è necessario utilizzare 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() 

ATTENZIONE: nessuno di codice di cui sopra è stato testato, o anche controllato!

+0

Il secondo approccio ha funzionato bene per me. Penso che tu faccia a 'a meno che (condizione) $ do' per compilare (nota il '$') – kunigami

2

Non ho mai lavorato con Haskell, ma sembra facile. Prova when (isNothing date) $ exit(). Se anche questo non funziona, assicurati che la tua affermazione sia corretta. Vedi anche questo sito per maggiori informazioni: Breaking From loop.

+4

Buon collegamento, ma si noti che 'exit' è definito nell'esempio e non è un builtin. La soluzione in questo post è la stessa della soluzione di @ dave4420: un trasformatore monad. – luqui

12

Utilizzare un trasformatore 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() 

Per una maggiore flessibilità nel valore di ritorno quando rientro anticipato, utilizzare throwError/eitherT/EitherT invece di mzero/maybeT/MaybeT. (Anche se poi non è possibile utilizzare guard.)

(Probabilmente anche usare headZ invece di headMay e fosso il esplicito guard.)

+1

A cosa serve 'Control.Error.Util'? –

+1

@Joehillen 'forseT'. – dave4420

Problemi correlati