2011-09-28 31 views
3

我在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) 
+3

添加類型將幫助您瞭解發生了什麼事情。請注意,您有3個字段,每個字段有兩個字段,您將得到2^3 = 8個結果。這是由於列表monad的工作方式 - 它給你笛卡爾的產品。 – rampion

+0

謝謝。我花了一些時間來找出我錯在哪裏,並且有效地理解了這裏的應用仿函數。我現在沒有正確的代碼。我可能必須更改我的數據結構以更好地匹配xml結構。 –

+4

我認爲你需要zipList應用實例,而不是默認的。 –

回答

1

正如rampion指出的那樣,問題在於list monad如何與applicative配合使用。看看這個:

λ *Main > (+) <$> [1,2,3] <*> [1,2,3] 
[2,3,4,3,4,5,4,5,6] 

結果是(+)適用​​於[1,2,3]和[1,2,3]的carthesian乘積:結果列表有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"}] 
相關問題