سؤال
جميع الأمثلة التي رأيتها حتى الآن باستخدام مجموعة أدوات Haskell XML ، HXT ، تستخدم runX
لتنفيذ المحلل. runX
يدير داخل موناد IO. هل هناك طريقة لاستخدام محلل XML هذا خارج IO؟ يبدو أن عملية خالصة بالنسبة لي ، لا تفهم لماذا أجبرت على أن أكون داخل IO.
المحلول
يمكنك استخدام HXT's xread
جنبا إلى جنب مع runLA
لتحليل سلسلة XML خارج IO
.
xread
لديه النوع التالي:
xread :: ArrowXml a => a String XmlTree
هذا يعني أنه يمكنك تكوينه بأي سهم من النوع (ArrowXml a) => a XmlTree Whatever
للحصول على a String Whatever
.
runLA
يشبه runX
, ، ولكن للأشياء من النوع LA
:
runLA :: LA a b -> a -> [b]
LA
هو مثال ArrowXml
.
لوضع كل هذا معًا ، النسخة التالية من إجابتي لسؤالك السابق يستخدم HXT لتحليل سلسلة تحتوي على XML تم تشكيلها جيدًا دون أي IO
متضمن:
{-# 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
و getValues
تشبه الإصدار السابق ، مع بعض التغييرات البسيطة لتناسب المدخلات والمخرجات المتوقعة. الفرق الرئيسي هو أننا هنا نستخدمه xread
و runLA
بدلاً من readString
و runX
.
سيكون من الجيد أن تكون قادرًا على قراءة شيء مثل كسول ByteString
بطريقة مماثلة ، ولكن بقدر ما أعلم أن هذا غير ممكن حاليًا مع HXT.
بضعة أشياء أخرى: أنت يستطيع تحليل الأوتار بهذه الطريقة بدون IO
, ، لكن من الأفضل استخدامها runX
كلما استطعت: يمنحك المزيد من التحكم في تكوين المحلل ، ورسائل الخطأ ، إلخ.
أيضًا: حاولت صنع الرمز في المثال واضح ومباشر ، لكن المدمجون في Control.Arrow
و Control.Arrow.ArrowList
اجعل من الممكن العمل مع Arrows بشكل أكبر إذا أردت. فيما يلي تعريف مكافئ لـ classes
, ، فمثلا:
classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
where pairs = getAttrValue "class" &&& deep getText
نصائح أخرى
كانت إجابة ترافيس براون مفيدة للغاية. أريد فقط إضافة الحل الخاص بي هنا ، والذي أعتقد أنه أكثر عمومية (باستخدام نفس الوظائف ، فقط تجاهل المشكلات الخاصة بالمشكلة).
كنت في السابق غير متوفرة مع:
upIO :: XmlPickler a => String -> IO [a]
upIO str = runX $ readString [] str >>> arrL (maybeToList . unpickleDoc xpickle)
الذي تمكنت من تغييره إلى هذا:
upPure :: XmlPickler a => String -> [a]
upPure str = runLA (xreadDoc >>> arrL (maybeToList . unpickleDoc xpickle)) str
وأنا أتفق معه تمامًا على أن القيام بذلك يمنحك تحكمًا أقل في تكوين المحلل ، وهو أمر مؤسف.