我在Andrew Birkett的博客Applicative arrows for XML &&& return to pure上读到,我们可以混合使用箭头和应用程序。
我自己尝试过,但我没有想到的。 我想要这个结果:
[Scenario {scenario = "11111", origin = "333", alarm = "Sonde1"},
Scenario {scenario = "22222", origin = "444", alarm = "Sonde2"}]
但我得到了这个:
[Scenario {scenario = "11111", origin = "333", alarm = "Sonde1"},
Scenario {scenario = "11111", origin = "333", alarm = "Sonde2"},
Scenario {scenario = "11111", origin = "444", alarm = "Sonde1"},
Scenario {scenario = "11111", origin = "444", alarm = "Sonde2"},
Scenario {scenario = "22222", origin = "333", alarm = "Sonde1"},
Scenario {scenario = "22222", origin = "333", alarm = "Sonde2"},
Scenario {scenario = "22222", origin = "444", alarm = "Sonde1"},
Scenario {scenario = "22222", origin = "444", alarm = "Sonde2"}]
我认为我的代码有一个转折,但我不知道在哪里搜索。
以下是我的代码,如果有人可以提供一些帮助。
{-# LANGUAGE Arrows, NoMonomorphismRestriction #-}
import Text.XML.HXT.Core
import Control.Applicative
import Text.XML.HXT.Arrow.ReadDocument
import Data.Maybe
import Text.XML.HXT.XPath.Arrows
import Text.Printf
data Scenario = Scenario
{ scenario, origin, alarm :: String
}
deriving (Show, Eq)
xml= "<DATAS LANG='en'>\
\ <SCENARIO ID='11111'>\
\ <ORIGIN ID='333'>\
\ <SCENARIO_S ERR='0'></SCENARIO_S>\
\ <SCENARIO_S ERR='2'></SCENARIO_S>\
\ <ALARM_M NAME='Sonde1'></ALARM_M>\
\ </ORIGIN>\
\ </SCENARIO>\
\ <SCENARIO ID='22222'>\
\ <ORIGIN ID='444'>\
\ <SCENARIO_S ERR='10'></SCENARIO_S>\
\ <SCENARIO_S ERR='12'></SCENARIO_S>\
\ <ALARM_M NAME='Sonde2'></ALARM_M>\
\ </ORIGIN>\
\ </SCENARIO>\
\</DATAS>"
parseXML string = readString [ withValidate no
, withRemoveWS yes -- throw away formating WS
] string
parseVal tag name = WrapArrow $ getXPathTrees (printf "/DATAS/%s" tag) >>> getAttrValue name
parseDatas = unwrapArrow $ Scenario <$> parseVal "SCENARIO" "ID"
<*> parseVal "SCENARIO/ORIGIN" "ID"
<*> parseVal "SCENARIO/ORIGIN/ALARM_M" "NAME"
testarr1= runX (parseXML xml >>> parseDatas)
答案 0 :(得分:1)
正如猖獗所指出的,问题是列表monad如何与applicative一起使用。看看这个:
λ *Main > (+) <$> [1,2,3] <*> [1,2,3]
[2,3,4,3,4,5,4,5,6]
结果是(+)的carthesian乘积应用于[1,2,3]和[1,2,3]:结果列表有9个元素。
在您的代码中,parseVal "SCENARIO" "ID"
将返回包含2个元素的列表,parseVal "SCENARIO/ORIGIN" "ID"
和parseVal "SCENARIO/ORIGIN/ALARM_M" "NAME"
也会如此。因此,结果将有8个元素。
相反,这就是我改变代码的方式:
--- parse a generic tag
parseVal tag name = WrapArrow $ getXPathTrees (printf "%s" tag) >>> getAttrValue name
--- parse a "SCENARIO" xml element
parseScenario = unwrapArrow $ Scenario
<$> (WrapArrow $ getAttrValue "ID")
<*> (parseVal "SCENARIO/ORIGIN" "ID")
<*> (parseVal "SCENARIO/ORIGIN/ALARM_M" "NAME")
--- parse the XML, extract a list of SCENARIOS and, for each, apply parseScenario
testarr1= runX (parseXML xml >>> getXPathTrees (printf "/DATAS/SCENARIO" ) >>> parseScenario)
结果符合要求:
λ *Main > testarr1
[Scenario {scenario = "11111", origin = "333", alarm = "Sonde1"},Scenario {scenario = "22222", origin = "444", alarm = "Sonde2"}]