3

私は現在、それを使用してGPXファイルHXTを解析することで学習しています。例はこちらです。私はこれまでに次のものを持っています:

import Data.Time
import Text.XML.HXT.Core

data Gpx    = Gpx [Trk]           deriving (Show)
data Trk    = Trk [TrkSeg]        deriving (Show)
data TrkSeg = TrkSeg [TrkPt]      deriving (Show)
data TrkPt  = TrkPt Double Double deriving (Show)

parseGpx =
  getChildren >>> isElem >>> hasName "gpx" >>>
  getChildren >>> isElem >>> hasName "trk" >>>
  parseGpxTrk >>> arr Gpx

parseGpxTrk = undefined
parseGpxTrkSegs = undefined

不完全であることがわかりますが、それでも型チェックを行う必要があります。残念ながら、私はすでにエラーに遭遇しています:

Couldn't match type ‘Trk’ with ‘[Trk]’
Expected type: Trk -> Gpx
  Actual type: [Trk] -> Gpx
In the first argument of ‘arr’, namely ‘Gpx’
In the second argument of ‘(>>>)’, namely ‘arr Gpx’

parseGpxTrkこのエラーが示すのは、一致した各項目を矢印からコンストラクターを介して渡そうとしていることですがarr Gpx、実際に必要なのは、arr Gpxコンストラクターを介して一致のリスト全体を渡すことです。

では、コンストラクターを介してリスト内の各エントリを渡すのではなく、コンストラクターを介してリストHXTとして一致を渡すにはどうすればよいでしょうか(または一般的には矢印ですか?) 。arr Gpxarr Gpx

4

1 に答える 1

1

ここに私にとってかなり良いと思われる解決策があります

{-# LANGUAGE Arrows #-}

import Data.Maybe
import Text.Read
import Text.XML.HXT.Core
import Control.Applicative

data Gpx    = Gpx [Trk]           deriving (Show)
data Trk    = Trk [TrkSeg]        deriving (Show)
data TrkSeg = TrkSeg [TrkPt]      deriving (Show)
data TrkPt  = TrkPt Double Double deriving (Show)

最も難しいのはおそらく、parseTrkPt正しく行うためにStrings から への解析を処理する必要があるためDoubleです。これは失敗する可能性があります。代わりにa を返すように決定し、Maybe TrkPtそれをさらに下に処理します。

elemsNamed :: ArrowXml cat => String -> cat XmlTree XmlTree
elemsNamed name = isElem >>> hasName name

parseTrkPt :: ArrowXml cat => cat XmlTree (Maybe TrkPt)
parseTrkPt = elemsNamed "trkpt" >>>
    proc trkpt -> do
        lat <- getAttrValue "lat" -< trkpt
        lon <- getAttrValue "lon" -< trkpt
        returnA -< TrkPt <$> readMaybe lat <*> readMaybe lon

procここでも構文を使用しました。にはTrkPt <$> readMaybe lat <*> readMaybe lon型がMaybe TrkPtあり、 のいずれかが を返したNothing場合にreadMaybe戻りますNothing。これで、すべての成功した結果を集計できます。

parseTrkSeg :: (ArrowXml cat, ArrowList cat) => cat XmlTree TrkSeg
parseTrkSeg =
    elemsNamed "trkseg" >>>
    (getChildren >>> parseTrkPt >>. catMaybes) >. TrkSeg

ここでは括弧が重要です。その部分を理解するのにしばらく時間がかかりました。かっこを配置する場所に応じて、[TrkSeg [TrkPt a b], TrkSeg [TrkPt c d]]代わりになど、異なる結果が得られます[TrkSeg [TrkPt a b, TrkPt c d]]。次のパーサーは、どちらも同様のパターンに従って単純です。

parseTrk :: ArrowXml cat => cat XmlTree Trk
parseTrk =
    elemsNamed "trk" >>>
    (getChildren >>> parseTrkSeg) >. Trk

parseGpx :: ArrowXml cat => cat XmlTree Gpx
parseGpx =
    elemsNamed "gpx" >>>
    (getChildren >>> parseTrk) >. Gpx

その後、非常に簡単に実行できますが、ルート要素をドリルダウンする必要があります。

main :: IO ()
main = do
    gpxs <- runX $ readDocument [withRemoveWS yes] "ana.gpx"
                >>> getChildren
                >>> parseGpx
    -- Pretty print the document
    forM_ gpxs $ \(Gpx trks) -> do
        putStrLn "GPX:"
        forM_ trks $ \(Trk segs) -> do
            putStrLn "\tTRK:"
            forM_ segs $ \(TrkSeg pts) -> do
                putStrLn "\t\tSEG:"
                forM_ pts $ \pt -> do
                    putStr "\t\t\t"
                    print pt

秘訣は、特にtype を持つArrowListtypeclass でメソッドを使用することです。から要素を集約し、それを新しい型に変換する関数に渡し、その新しい型で新しいを出力します。>.a b c -> ([c] -> d) -> a b dArrowListArrowListd

必要に応じて、最後の 3 つのパーサーについてこれを少し抽象化することもできます。

nestedListParser :: ArrowXml cat => String -> cat XmlTree a -> ([a] -> b) -> cat XmlTree b
nestedListParser name subparser constructor
    =   elemsNamed name
    >>> (getChildren >>> subparser)
    >.  constructor

parseTrkSeg :: (ArrowXml cat, ArrowList cat) => cat XmlTree TrkSeg
parseTrkSeg = nestedListParser "trkseg" (parseTrkPt >>. catMaybes) TrkSeg

parseTrk :: ArrowXml cat => cat XmlTree Trk
parseTrk = nestedListParser "trk" parseTrkSeg Trk

parseGpx :: ArrowXml cat => cat XmlTree Gpx
parseGpx = nestedListParser "gpx" parseTrk Gpx

これは、GPX ファイルの残りの文法を完成させたい場合に便利です。

于 2015-05-08T03:05:18.190 に答える