Подтвердить что ты не робот

Практика кодирования для F #

Я занимаюсь F # в Visual Studio 2010. Я разработчик с большим опытом разработки кода и архитектуры в объектно-ориентированных языках, таких как С# и Java.

Чтобы расширить свой набор навыков и помочь принять правильные решения, я пытаюсь использовать разные языки для разных целей. В частности, получите "правильную" кодировку с использованием функциональных языков (в данном случае F #).

Простым примером является создание некоторого XML, а затем добавление некоторых фильтров для устранения некоторых элементов.

Вот мой код:

open System
open System.Xml.Linq


let ppl:(string * string) list = [
    ("1", "Jerry"); 
    ("2", "Max"); 
    ("3", "Andrew");
]

/// Generates a Person XML Element, given a tuple.
let createPerson (id:string, name:string) = new XElement(XName.Get("Person"),
                                                new XAttribute(XName.Get("ID"), id),
                                                new XElement(XName.Get("Name"), name)
)

/// Filter People by having odd ID's
let oddFilter = fun (id:string, name:string) -> (System.Int32.Parse(id) % 2).Equals(1) 

/// Open filter which will return all people
let allFilter = fun (id:string, name:string) -> true

/// Generates a People XML Element.
let createPeople filter = new XElement(XName.Get("People"), 
                                ppl |> List.filter(filter)  |> List.map createPerson
)

/// First XML Object
let XmlA = createPeople oddFilter

/// Second XML Object
let XmlB = createPeople allFilter


printf "%A\n\n%A" XmlA XmlB


/// Waits for a keypress
let pauseKey = fun () -> System.Console.ReadKey() |> ignore


pauseKey()

Мои вопросы: что я сделал хорошо в этом сценарии? Какие части можно было бы сделать лучше?

Я действительно с нетерпением жду некоторых идей, и я очень рад тому, что знаком с функциональными парадигмами!:)

Заранее спасибо

4b9b3361

Ответ 1

В принципе, ваш код в порядке.

Есть только некоторые моменты, которые можно упростить с синтаксической точки зрения.

let ppl:(string * string) list = [
    ("1", "Jerry"); 
    ("2", "Max"); 
    ("3", "Andrew");
]

Компилятор сам выводит большинство типов:

let ppl = [ "1", "Jerry";
            "2", "Max";
            "3", "Andrew" ]

И, конечно же, вы можете переписать свои фильтры следующим образом currying:

let oddFilter (id:string, name:string) = (int id) % 2 = 1
let allFilter (id:string, name:string) = true

Самое большое улучшение - это отделить индексы от имен и позволить программе делать нумерацию. Вам не нужно работать со строками, а не цифрами, и вы можете использовать более идиоматические функции, не требующие кортежа:

let ppl = [ "Jerry"; "Max"; "Andrew" ]

let oddFilter id name = id % 2 = 1
let allFilter id name = true

let createPerson id name = ...

Часть

ppl |> List.filter(filter)  |> List.map createPerson

будет переписано на

[ for (index, name) in List.mapi (fun i x -> (i, x)) do
      if filter index name then
          yield createPerson (string index) name ]

Ответ 2

let createPeople filter = new XElement(XName.Get("People"), 
                            ppl |> List.filter(filter)  |> List.map createPerson
)

Эта часть может быть deforested вручную, или вы можете надеяться, что компилятор обезвредит ее вам.

В принципе, существует промежуточная структура (список фильтрованных людей), которая, если она скомпилирована наивно, будет выделена для обслуживания только один раз. Лучше применять createPerson для каждого элемента, как это принято в случае, если они находятся или выходят, и напрямую строить окончательный результат.

EDIT: cfern внес эту обезжиренную версию createPeople:

let createPeople filter = 
  new XElement(
    XName.Get("People"), 
    List.foldBack 
      (fun P acc -> if filter P then (createPerson P)::acc else acc) 
      ppl 
      [])

ПРИМЕЧАНИЕ: поскольку в filter или createPerson могут быть побочные эффекты, в F # довольно сложно, чтобы компилятор решил обезвредить сам по себе. В этом случае мне кажется, что обезлесение правильное, потому что даже если filter имеет побочные эффекты, createPerson не работает, но я не специалист.

Ответ 3

В большинстве случаев обезлесение без какой-либо конкретной причины, как правило, является плохой идеей. Какой из них вы считаете более легким для чтения и менее подверженным ошибкам? Обезлесение, извлеченное из контекста, просто добавляет сложность и/или связь с вашим кодом.

let createPeople filter ppl =
    ppl 
    |> List.mapi (fun i x -> (i, x)) 
    |> List.filter filter
    |> List.map createPerson

let createPeople filter ppl =
    [ for (index, name) in ppl |> List.mapi (fun i x -> (i, x)) do
          if filter (index, name) then
              yield createPerson (index, string) ]

let createPeople filter ppl = 
    (ppl |> List.mapi (fun i x -> (i, x)), []) 
    ||> List.foldBack (fun P acc -> if filter P then (createPerson P)::acc else acc) 

Как только вы привыкнете к синтаксической композиции, вы можете сбросить ppl.

let createPeople filter =
    List.mapi (fun i x -> (i, x)) 
    >> List.filter filter
    >> List.map createPerson

Все из них используют данные с чередованием.

let filter (id, name) = 
    id % 2 = 1

let allFilter (id, name) = 
    true

let createPerson (id, name) = 
    ()

Ответ 4

Мне также недавно понадобилось преобразовать XSL в файл XML. Это F # я использовал для этого.

Есть некоторые интересные причуды с использованием методов .net.

(* Transforms an XML document given an XSLT. *)

open System.IO
open System.Text
open System.Xml
open System.Xml.Xsl

let path = @"C:\\XSL\\"

let file_xml = path + "test.xml"
let file_xsl = path + "xml-to-xhtml.xsl"

(* Compile XSL file to allow transforms *)
let compile_xsl (xsl_file:string) = new XslCompiledTransform() |> (fun compiled -> compiled.Load(xsl_file); compiled)
let load_xml (xml_file:string) = new XmlDocument() |> (fun doc -> doc.Load(xml_file); doc)

(* Transform an Xml document given an XSL (compiled *)
let transform (xsl_file:string) (xml_file:string) = 
      new MemoryStream()
        |> (fun mem -> (compile_xsl xsl_file).Transform((load_xml xml_file), new XmlTextWriter(mem, Encoding.UTF8)); mem)
        |> (fun mem -> mem.Position <- (int64)0; mem.ToArray())

(* Return an Xml fo document that has been transformed *)
transform file_xsl file_xml
    |> (fun bytes -> File.WriteAllBytes(path + "out.html", bytes))