Сканирование XML с C# на F#

Пытаясь изучить F#, и я попытался переопределить следующую функцию в F#

private string[] GetSynonyms(string synonyms)
{
    var items = Enumerable.Repeat(synonyms, 1)
                          .Where(s => s != null)
                          .Select(XDocument.Parse)
                          .Select(doc => doc.Root)
                          .Where(root => root != null)
                          .SelectMany(e => e.Elements(SynonymsNamespace + "synonym"))
                          .Select(e => e.Value)
                          .ToArray();

    return items;
}

Я получил это далеко сам

let xname = XNamespace.Get "http://localuri"

let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"

let synonyms str =
    let items = [str]
    items
    |> List.map System.Xml.Linq.XDocument.Parse
    |> List.map (fun x -> x.Root)
    |> List.map (fun x -> x.Elements(xname + "synonym") |> Seq.cast<System.Xml.Linq.XElement>)
    |> Seq.collect (fun x -> x)
    |> Seq.map (fun x -> x.Value)

let a = synonyms syn

Dump a

Теперь мне интересно, есть ли более функциональный способ написания того же кода.

Извлекая доступ к свойствам автономных функций, я получил эту версию

let xname = XNamespace.Get "http://localuri"

let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"

let getRoot (doc:System.Xml.Linq.XDocument) = doc.Root

let getValue (element:System.Xml.Linq.XElement) = element.Value

let getElements (element:System.Xml.Linq.XElement) =
    element.Elements(xname + "synonym")
        |> Seq.cast<System.Xml.Linq.XElement>

let synonyms str =
    let items = [str]
    items
    |> List.map System.Xml.Linq.XDocument.Parse
    |> List.map getRoot
    |> List.map getElements
    |> Seq.collect (fun x -> x)
    |> Seq.map getValue

let a = synonyms syn

Dump a

Но у меня все еще есть некоторые проблемы

  1. Могу ли я переписать этот Seq.collect (fun x -> x) по-другому? Это звучит излишне
  2. Могу ли я удалить все это (весело x -> x.Property) без создания новых функций?
  3. Как на самом деле вернуть массив, а не Seq<'a> (я так понимаю, это IEnumerable<' a>)

Спасибо

2 ответа

Решение
  1. Seq.collect (fun x -> x) можно переписать с помощью предопределенной функции id в идентификатор Seq.collect.

  2. В F# 4.0 его можно удалить только для конструкторов.

  3. использовать Seq.toArray или Seq.toList

Было бы очень неправильно отбросить C#-код и пойти ва-банк с провайдером XML в F#? В моем мире всегда неправильно анализировать XML, когда существуют другие решения (если только я не пытаюсь создать восьмиугольные колеса или влажные порохы, которые другие сделали лучше до меня).

В этом отношении я бы даже использовал какое-то преобразование (XSLT) или выделение (XPATH/XQUERY), если бы я не мог использовать XML-провайдера или какой-либо XSD (C#) для генерации кода.

Если по какой-то причине XML так неструктурирован, что вам действительно нужен синтаксический анализ, то, возможно, XML ошибочен...

При использовании XmlProvider вы получаете пространство имен, типы и т. Д. Бесплатно...

#r @"..\correct\this\path\to\packages\FSharp.Data.2.2.5\lib\net40\FSharp.Data.dll"
#r "System.Xml.Linq"


open FSharp.Data
[<Literal>]
let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"

type Synonyms = XmlProvider<syn>

let a = Synonyms.GetSample()

a.Synonyms |> Seq.iter (printfn "%A")

Помните, что XmlProvider также может принимать файлы или URL-адреса в качестве примеров для вывода типов и т. Д., И что вы также можете использовать этот код в качестве примера и затем использовать

let a = Synonyms.Load(stuff)

где вещи - это чтение из потока, чтения текста или URI и вывод в соответствии с вашим примером. Образец и материал могут даже указывать на один и тот же файл /Uri, если это стандартное размещение данных.

Смотрите также: http://fsharp.github.io/FSharp.Data/library/XmlProvider.html

Другие вопросы по тегам