1

Haskell で単純な XML パーサーを作成しました。関数convertXMLは、XML ファイルのコンテンツを受け取り、さらに処理される抽出された値のリストを返します。

XMLタグの1つの属性に商品画像のURLも含まれており、タグが見つかればダウンロードもできるように機能を拡張したいと考えています。

convertXML ::  (Text.XML.Light.Lexer.XmlSource s) => s -> [String]
convertXML xml = productToCSV products
    where
        productToCSV [] = []
        productToCSV (x:xs) = (getFields x) ++ (productToCSV
                                (elChildren x)) ++ (productToCSV xs)
        getFields elm = case (qName . elName) elm of
                            "product" -> [attrField "uid", attrField "code"]
                            "name" -> [trim $ strContent elm]
                            "annotation" -> [trim $ strContent elm]
                            "text" -> [trim $ strContent elm]
                            "category" -> [attrField "uid", attrField "name"]
                            "manufacturer" -> [attrField "uid",
                                                attrField "name"]
                            "file" -> [getImgName]
                            _ -> []
            where
                attrField fldName = trim . fromJust $
                                        findAttr (unqual fldName) elm
                getImgName = if (map toUpper $ attrField "type") == "FULL"
                                then
                                    -- here I need some IO code
                                    -- to download an image
                                    -- fetchFile :: String -> IO String
                                    attrField "file"
                                else []
        products = findElements (unqual "product") productsTree
        productsTree = fromJust $ findElement (unqual "products") xmlTree
        xmlTree = fromJust $ parseXMLDoc xml

getImgName関数に IO コードを挿入する方法、または convertXML 関数を不純なバージョンに完全に書き直す必要がありますか?

UPDATE II convertXML 関数の最終バージョン。カールによって提案されたハイブリッドの純粋/不純だがクリーンな方法。返されたペアの 2 番目のパラメーターは、イメージのダウンロードとディスクへの保存を実行し、イメージが保存されているローカル パスのリストをラップする IO アクションです。

convertXML ::  (Text.XML.Light.Lexer.XmlSource s) => s -> ([String], IO [String])
convertXML xml = productToCSV products (return [])
    where
        productToCSV :: [Element] -> IO String -> ([String], IO [String])
        productToCSV [] _ = ([], return [])
        productToCSV (x:xs) (ys) = storeFields (getFields x)
                            ( storeFields (productToCSV (elChildren x) (return []))
                                (productToCSV xs ys) )
        getFields elm = case (qName . elName) elm of
                            "product" -> ([attrField "uid", attrField "code"], return [])
                            "name" -> ([trim $ strContent elm], return [])
                            "annotation" -> ([trim $ strContent elm], return [])
                            "text" -> ([trim $ strContent elm], return [])
                            "category" -> ([attrField "uid", attrField "name"], return [])
                            "manufacturer" -> ([attrField "uid",
                                                attrField "name"], return [])
                            "file" -> getImg
                            _ -> ([], return [])
            where
                attrField fldName = trim . fromJust $
                                        findAttr (unqual fldName) elm
                getImg = if (map toUpper $ attrField "type") == "FULL"
                            then
                                ( [attrField "file"], fetchFile url >>=
                                    saveFile localPath >>
                                    return [localPath] )
                                else ([], return [])
                    where
                        fName = attrField "file"
                        localPath = imagesDir ++ "/" ++ fName
                        url = attrField "folderUrl" ++ "/" ++ fName

        storeFields (x1s, y1s) (x2s, y2s) = (x1s ++ x2s, liftM2 (++) y1s y2s)
        products = findElements (unqual "product") productsTree
        productsTree = fromJust $ findElement (unqual "products") xmlTree
        xmlTree = fromJust $ parseXMLDoc xml
4

3 に答える 3

4

より良いアプローチは、関数が結果の一部としてダウンロードするファイルのリストを返すようにすることです。

convertXML ::  (Text.XML.Light.Lexer.XmlSource s) => s -> ([String], [URL])

別の関数でそれらをダウンロードします。

于 2011-01-02T13:28:33.370 に答える
3

Haskell の型システムの要点は、IO アクション (型 IO a の値) を除いて IO を実行できないことです。これに違反する方法はありますが、最適化や遅延評価との相互作用により、予想とはまったく異なる動作をするリスクがあります。したがって、IO がそのように機能する理由を理解するまでは、別の方法で機能させようとしないでください。

しかし、この設計の非常に重要な結果は、IO アクションがファースト クラスであるということです。少し賢くすると、関数を次のように書くことができます。

convertXML ::  (Text.XML.Light.Lexer.XmlSource s) => s -> ([String], IO [Image])

ペアの 2 番目の項目は、実行されると、存在するイメージのリストを提供する IO アクションになります。これにより、convertXML の外部にイメージをロードするコードが必要なくなり、実際にイメージが必要な場合にのみ IO を実行できるようになります。

于 2011-01-02T15:19:43.730 に答える
2

私は基本的にアプローチに見えます:

  1. 関数が見つかった画像のリストも提供し、後でそれらを不純な関数で処理します。怠惰が残りを行います。
  2. 獣全体を不浄にする

私は通常、最初のアプローチの方が好きです。d

于 2011-01-02T13:27:44.250 に答える