سؤال

جميع الأمثلة التي رأيتها حتى الآن باستخدام مجموعة أدوات 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

وأنا أتفق معه تمامًا على أن القيام بذلك يمنحك تحكمًا أقل في تكوين المحلل ، وهو أمر مؤسف.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top