2009-12-13 9 views
12

Ich habe mit F # in Visual Studio 2010 getüftelt. Ich bin ein Entwickler mit mehr Code/Architektur Design-Erfahrung in objektorientierten Sprachen wie C# und Java.Coding Practice für F #

Um meine Fähigkeiten zu erweitern und bessere Entscheidungen zu treffen, versuche ich verschiedene Sprachen, um verschiedene Dinge zu tun. Insbesondere die Kodierung "richtig" mit funktionalen Sprachen (in diesem Fall F #) bekommen.

Ein einfaches Beispiel erzeugt etwas XML und fügt dann einige Filter hinzu, um einige Elemente zu beseitigen.

Hier ist mein Code:

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() 

Meine Fragen sind: Welche Dinge habe ich in diesem Szenario gut gemacht? Welche Teile könnten besser gemacht werden?

Ich freue mich schon sehr auf ein paar Ideen und freue mich auch darauf, mit funktionalen Paradigmen vertraut zu werden! :)

Vielen Dank im Voraus

+0

+1 In der genau gleichen Situation hier (aber mit anderen Code offensichtlich) gut sein, die Antwort darauf zu sehen. – Jay

+1

F # ist grundsätzlich so, dass es Zahlen für Sie, es hat eine sehr starke Inferenz-Engine, so sollten Sie sich daran gewöhnt Typ Annotation wegzulassen und Sie als generische wie Sie können – 0xFF

+0

@martain_net, danke für den Tipp, das macht eine Menge Sinn tatsächlich :) Ich denke, ich habe die Typdefinition überstrapaziert, um die verschiedenen nativen F # -Typen zu verstehen (zB (string * string) liste bedeutet Liste von Tupeln mit 2 Strings). – Russell

Antwort

13

Im Prinzip ist Ihr Code in Ordnung.

Es gibt nur einige Punkte, die aus syntaktischer Sicht vereinfacht werden können.

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

Der Compiler kann die meisten Arten von sich ableiten:

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

Und natürlich können Sie neu schreiben Ihre Filter wie dies aufgrund currying:

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

Die größte Verbesserung würde die Indizes von den Namen trennen und das Programm die Nummerierung machen lassen.Sie müssen nicht anstelle von Zahlen mit Strings arbeiten und mehr idiomatische Tupel freie Funktionen nutzen können:

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

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

let createPerson id name = ... 

Der Teil

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

zu

neu geschrieben werden würde
[ for (index, name) in List.mapi (fun i x -> (i, x)) do 
     if filter index name then 
      yield createPerson (string index) name ] 
+2

Yup, Inferenz und Curry eingeben. Gute Sachen. – cfern

+0

Danke Dario, es gibt ein paar gute Ideen. Stört es dich, wenn ich frage, warum du die ppl |> List.filter (filter) |> List.map createPerson umschreiben würdest? Ich bin etwas verwirrt von der Verarbeitung. Sieht aus, als gäbe es einige Themen, die ich untersuchen werde.:) Thx nochmal – Russell

+0

Der Punkt ist nur, mit den Indizes umzugehen wo sie wirklich gebraucht werden. Also erzeuge ich sie * in * createPeople'. – Dario

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

Dieser Teil deforested manuell sein kann, oder Sie können hoffen, dass der Compiler deforest es für Sie.

Grundsätzlich gibt es eine Zwischenstruktur (die Liste der gefilterten Personen), die, wenn dies naiv übersetzt wird, nur einmal vergeben wird. Wenden Sie besser createPerson auf jedes Element an, wenn entschieden wird, ob sie ein- oder ausgehen, und erstellen Sie das Endergebnis direkt.

EDIT: cfern trug diese abgeholzten Version von createPeople:

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

Hinweis: Da es in filter oder createPerson Nebenwirkungen sein könnte, in F # ist es ziemlich schwer für den Compiler zu deforest zu entscheiden, durch selbst. In diesem Fall scheint mir, dass die Entwaldung korrekt ist, denn selbst wenn filter Nebenwirkungen hat, ist createPerson nicht, aber ich bin kein Spezialist.

+2

Ich kann das jetzt nicht testen, aber könnten Sie List.filter und List.map durch Seq.filter und Seq.map ersetzen, um Zwischenlisten zu vermeiden? – cfern

+0

@cfern Wenn Sie immer noch einen 'Filter' gefolgt von einer' map' ausführen, gibt es immer noch eine Zwischenstruktur, die zugewiesen, einmal verwendet und dann verworfen wird. Ich dachte eher an die Linie, sie beide durch eine einzige "Falte" zu ersetzen, die "createPerson" anwendet und das Ergebnis nur dann zum Akkumulator hinzufügt, wenn "Filter" positiv getestet wird. –

+1

Ah, ich verstehe. Ich bin noch neu in der funktionalen Programmierung und daher noch nicht vollständig zum Falten. Denkst du darüber nach? ... createPeople Filter = neues XElement (XName.Get ("Leute"), List.foldBack (Spaß P acc -> wenn Filter P dann (createPerson P) :: acc sonst acc) ppl []) ... where Ich habe foldBack verwendet, um die Reihenfolge der Personen zu erhalten. – cfern

3

meisten von der Zeit zu entwalden ohne einen bestimmten Grund, in der Regel Leistung, ist eine schlechte Idee. Welche davon sehen Sie als leichter lesbar und weniger fehleranfällig an? Entwaldung, die aus dem Zusammenhang gerissen wurde, fügt Ihrem Code Komplexität und/oder Kopplung hinzu.

Sobald Sie sich an die Syntax-Funktion Komposition können Sie ppl.

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

Alle diese verwenden getupfte Daten.

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

let allFilter (id, name) = 
    true 

let createPerson (id, name) = 
    () 
+0

danke gradbot. Aus Neugier, was bedeutet >> in F #? Ist das ein Rohrleitungsbetreiber wie |>? Oder ist es ein bitweiser Operator (Bits nach rechts verschieben)? – Russell

+0

Es verbindet zwei Funktionen zusammen mit der Funktion auf der linken Seite zuerst aufgerufen wird. Es ist definiert als let (>>) f g x = g (f x) – TimothyP

0

Ich musste auch kürzlich eine XSL in eine XML-Datei transformieren. Dies ist die F # Ich habe das getan.

Hat einige interessante Macken mit der Verwendung der. NET-Methoden.