2013-03-15 3 views
13

Ich versuche, für eine Webseite mit Haskell zu kratzen und die Ergebnisse in ein Objekt zu kompilieren.Wie mache ich eine Do-Block-Rückgabe früh?

Wenn, aus welchen Gründen auch immer, ich nicht alle Einzelteile von den Seiten erhalten kann, möchte ich aufhören zu versuchen, die Seite zu verarbeiten und früh zurückzukommen.

Zum Beispiel:

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

Das Problem ist die when nicht das Blockieren zu stoppen oder die anderen Teile verhindern, ausgeführt werden.

Was ist der richtige Weg?

+1

Ist das was du willst? http://www.haskellforall.com/2012/07/breaking-from-loop.html –

Antwort

14

return in Haskell nicht das Gleiche wie return in anderen Sprachen nicht. Stattdessen fügt return einen Wert in eine Monade ein (in diesem Fall IO). Sie haben ein paar Optionen

die einfachste, wenn

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

eine weitere Option zu verwenden ist 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() 

das allgemeine Problem hier zu verwenden ist, dass die IO Monade nicht über Steuerungseffekte (außer Ausnahmen). Auf der anderen Seite könnten Sie die vielleicht Monade 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() 

verwenden, wenn Sie Sie wirklich voll geblasen Steuereffekte wollen bekommen müssen 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() 

WARNUNG verwenden: keine der oben genannten Code wurde getestet oder sogar typgeprüft!

+0

Der zweite Ansatz funktionierte gut für mich. Ich glaube, du brauchst 'außer (Bedingung) $ do' für es zu kompilieren (beachten Sie die '$') – kunigami

2

Ich habe noch nie mit Haskell gearbeitet, aber es scheint so einfach zu sein. Versuchen Sie when (isNothing date) $ exit(). Wenn dies auch nicht funktioniert, vergewissern Sie sich, dass Ihre Aussage korrekt ist. Siehe auch diese Website für weitere Informationen: Breaking From loop.

+4

Gute Verbindung, aber beachten Sie, dass 'exit' im Beispiel definiert ist und nicht eingebaut ist. Die Lösung in diesem Post ist die gleiche wie @ dave4420's Lösung: ein Monad Transformer. – luqui

12

Verwenden Sie einen Monodentrafo!

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

Für mehr Flexibilität im Rückgabewert, wenn Sie vorzeitige Rückkehr verwenden throwError/eitherT/EitherT statt mzero/maybeT/MaybeT. (Obwohl, dann können Sie nicht guard verwenden.)

(Wahrscheinlich auch headZ statt headMay verwenden und die expliziten guard Graben.)

+1

Wofür wird 'Control.Error.Util' benötigt? –

+1

@Joehillen 'vielleichtT'. – dave4420