2010-10-10 13 views
27

Wszystkie przykłady, które widziałem do tej pory przy użyciu zestawu narzędzi Haskell XML, HXT, używają runX do wykonania analizatora składni. runX uruchamia się w Monadzie IO. Czy istnieje sposób użycia tego parsera XML poza IO? Wydaje się być dla mnie czystą operacją, nie rozumiem, dlaczego jestem zmuszony przebywać w IO.Uruchamianie Haskell HXT poza IO?

+1

Szybki rzut oka sprawia, że ​​wygląda na to, że 'runX' odczytuje plik XML i dlatego jest nieczytelnym IO. – alternative

+1

Myślę, że analizator składni HXT jest parserem "online", tzn. Nie musi czytać całego wejścia, aby rozpocząć produkcję. Plusem tego jest to, że (przynajmniej w zasadzie) może działać ze stałym śladem pamięci, wadą jest to, że musi czytać "fragmenty" sygnału wejściowego na żądanie, więc musi być w IO. –

+2

Ale czy nie można tego łatwo rozwiązać za pomocą leniwego IO? Na przykład, aby uzyskać wysokowydajne analizowanie XML, w tej chwili używam Hexpat (dla ByteStrings i lazy SAX-parsowania.) I polyparse (Poly.Lazy.) Otrzymuję stałe ślady pamięci, szybkie przetwarzanie i * wszystko * parsowanie odbywa się w czystych funkcjach! –

Odpowiedz

28

Możesz użyć HXT's xread wraz z runLA do parsowania łańcucha XML poza IO.

xread posiada następujące rodzaje:

xread :: ArrowXml a => a String XmlTree 

oznacza to, że można je komponować z dowolnej strzałki typu (ArrowXml a) => a XmlTree Whatever uzyskać a String Whatever.

runLA jest jak runX, ale do rzeczy typu LA:

runLA :: LA a b -> a -> [b] 

LA jest instancją ArrowXml.

Aby to umieścić wszystkie razem, następującą wersję my answer do poprzedniego pytania zastosowania HXT do analizowania ciąg zawierający dobrze uformowane XML bez IO zaangażowanych:

{-# LANGUAGE Arrows #-} 
module Main where 

import qualified Data.Map as M 
import Text.XML.HXT.Arrow 

classes :: (ArrowXml a) => a XmlTree (M.Map String String) 
classes = listA (divs >>> pairs) >>> arr M.fromList 
    where 
    divs = getChildren >>> hasName "div" 
    pairs = proc div -> do 
     cls <- getAttrValue "class" -< div 
     val <- deep getText   -< div 
     returnA -< (cls, val) 

getValues :: (ArrowXml a) => [String] -> a XmlTree (String, Maybe String) 
getValues cs = classes >>> arr (zip cs . lookupValues cs) >>> unlistA 
    where lookupValues cs m = map (flip M.lookup m) cs 

xml = "<div><div class='c1'>a</div><div class='c2'>b</div>\ 
     \<div class='c3'>123</div><div class='c4'>234</div></div>" 

values :: [(String, Maybe String)] 
values = runLA (xread >>> getValues ["c1", "c2", "c3", "c4"]) xml 

main = print values 

classes i getValues są podobne do poprzedniego wersja z kilkoma niewielkimi zmianami, aby dopasować oczekiwane wejście i wyjście. Główną różnicą jest to, że używamy tutaj xread i runLA zamiast readString i runX.

Byłoby miło móc przeczytać coś podobnego do leniwego ByteString w podobny sposób, ale o ile wiem, nie jest to obecnie możliwe z HXT.


Kilka innych rzeczy: ty może ciągi składniowy ten sposób bez IO, ale to chyba lepiej użyć runX kiedy można: to daje większą kontrolę nad konfiguracją parser, komunikatów o błędach , itp.

Ponadto: Starałem się, aby kod w tym przykładzie był prosty i łatwy do przedłużenia, ale kombinatory w Control.Arrow i Control.Arrow.ArrowList umożliwiają bardziej zwięzłą pracę ze strzałkami, jeśli chcesz. Poniżej jest równoważna definicja classes, na przykład: odpowiedź

classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList 
    where pairs = getAttrValue "class" &&& deep getText 
+0

Witaj Travis. To wspaniale. Twoja pomoc jest bardzo przydatna w mojej próbie zmierzenia się z HXT. – Muchin

1

Travis Brown był bardzo pomocny. Chcę tutaj dodać własne rozwiązanie, które moim zdaniem jest nieco bardziej ogólne (używając tych samych funkcji, po prostu ignorując problemy specyficzne dla problemu).

byłem poprzednio unpickling z:

upIO  :: XmlPickler a => String -> IO [a] 
upIO str = runX $ readString [] str >>> arrL (maybeToList . unpickleDoc xpickle) 

które udało mi się zmienić do tego:

upPure :: XmlPickler a => String -> [a] 
upPure str = runLA (xreadDoc >>> arrL (maybeToList . unpickleDoc xpickle)) str 

I całkowicie zgadzam się z nim, że robi to daje mniejszą kontrolę nad konfiguracją parser itp., co jest niefortunne.