2009-05-21 9 views
4

Gday Alle,F # Mutable zu Immutable

Ich habe in einigen F # in der letzten Zeit Dilettantismus und ich kam mit dem folgenden String-Builder, die ich von einigen C# -Code portiert. Es konvertiert ein Objekt in eine Zeichenfolge, sofern es eine in den Attributen definierte Regex übergibt. Es ist wahrscheinlich zu viel für die Aufgabe, aber es ist für Lernzwecke.

Momentan verwendet das BuildString-Member eine veränderbare Stringvariable updatedTemplate. Ich habe mir das Gehirn zerbrochen, um einen Weg zu finden, dies ohne irgendwelche veränderlichen Objekte ohne Erfolg zu tun. Was mich zu meiner Frage bringt.

Ist es möglich, die Memberfunktion BuildString ohne veränderbare Objekte zu implementieren?

Cheers,

Michael

//The Validation Attribute 
type public InputRegexAttribute public (format : string) as this = 
    inherit Attribute() 
    member self.Format with get() = format 

//The class definition 
type public Foo public (firstName, familyName) as this = 
    [<InputRegex("^[a-zA-Z\s]+$")>] 
    member self.FirstName with get() = firstName 

    [<InputRegex("^[a-zA-Z\s]+$")>] 
    member self.FamilyName with get() = familyName 

module ObjectExtensions = 
    type System.Object with 
     member this.BuildString template = 
      let mutable updatedTemplate : string = template 
      for prop in this.GetType().GetProperties() do 
       for attribute in prop.GetCustomAttributes(typeof<InputRegexAttribute>,true).Cast<InputRegexAttribute>() do 
        let regex = new Regex(attribute.Format) 
        let value = prop.GetValue(this, null).ToString() 
        if regex.IsMatch(value) then 
         updatedTemplate <- updatedTemplate.Replace("{" + prop.Name + "}", value) 
        else 
         raise (new Exception "Regex Failed") 
      updatedTemplate 

open ObjectExtensions 
try 
    let foo = new Foo("Jane", "Doe") 
    let out = foo.BuildInputString("Hello {FirstName} {FamilyName}! How Are you?") 
    printf "%s" out 
with | e -> printf "%s" e.Message 

Antwort

1

Ich denke, man kann immer ein „for-Schleife über eine Sequenz mit nur einem Effekt (mutiert eine lokale Variable)“ in Code umwandeln, die von der wandelbaren entledigt; hier ist ein Beispiel für die allgemeine Transformation:

let inputSeq = [1;2;3] 

// original mutable 
let mutable x = "" 
for n in inputSeq do 
    let nStr = n.ToString() 
    x <- x + nStr 
printfn "result: '%s'" x 

// immutable 
let result = 
    inputSeq |> Seq.fold (fun x n -> 
     // the 'loop' body, which returns 
     // a new value rather than updating a mutable 
     let nStr = n.ToString() 
     x + nStr 
    ) "" // the initial value 
printfn "result: '%s'" result 

Ihr spezielles Beispiel Schleifen verschachtelt ist, also hier ist ein Beispiel für die gleiche Art von mechanischen zeigen in zwei Schritte verwandeln:

let inputSeq1 = [1;2;3] 
let inputSeq2 = ["A";"B"] 

let Original() = 
    let mutable x = "" 
    for n in inputSeq1 do 
     for s in inputSeq2 do 
      let nStr = n.ToString() 
      x <- x + nStr + s 
    printfn "result: '%s'" x 

let FirstTransformInnerLoopToFold() = 
    let mutable x = "" 
    for n in inputSeq1 do 
     x <- inputSeq2 |> Seq.fold (fun x2 s -> 
      let nStr = n.ToString() 
      x2 + nStr + s 
     ) x 
    printfn "result: '%s'" x 

let NextTransformOuterLoopToFold() = 
    let result = 
     inputSeq1 |> Seq.fold (fun x3 n -> 
      inputSeq2 |> Seq.fold (fun x2 s -> 
       let nStr = n.ToString() 
       x2 + nStr + s 
      ) x3 
     ) "" 
    printfn "result: '%s'" result 

(In dem obigen Code , habe ich die ‚x2‘ Namen und ‚x3‘ deutlicher zu machen Scoping, aber Sie können nur den Namen ‚x‘ im gesamten verwendet werden.)

Es kann sinnvoll sein, um zu versuchen, dieselbe zu tun, auf dem Beispiel-Code umwandeln und posten Sie Ihre eigene Antwort. Dies wird nicht unbedingt den idiomatischsten Code ergeben, kann aber eine Übung sein, um eine for-Schleife in eine Seq.-fold-Verbindung umzuwandeln.

(Das heißt, in diesem Beispiel das ganze Ziel ist meist eine akademische Übung - der Code mit dem wandelbar ist ‚fein‘.)

+0

Prost Brian und Jon . Ich werde die Falte implementieren und sehen, wie es sich herausstellt. Wie ich in der Frage erwähnt habe, ist dies eine rein akademische Übung. – Michael

1

ich dies als Code bis nicht die Zeit haben zu schreiben, aber:

  • Schreiben Sie eine Funktion, die innerste Teil darstellt, wobei ein string (das Ergebnis bisher) und ein Tupel der Eigenschaft und des Attributs und gibt die Zeichenfolge nach dem Ersetzen zurück.
  • Verwenden Sie seq.map_concat, um vom Array der Eigenschaften, die von GetProperties() zurückgegeben werden, in eine Folge von (Eigenschafts-, Attribut-) Tupeln zu wechseln.
  • Verwenden Sie seq.fold mit den vorherigen zwei Bits, um die gesamte Transformation durchzuführen, wobei die ursprüngliche Vorlage als Anfangswert für die Aggregation verwendet wird. Das Gesamtergebnis ist die endgültig ersetzte Zeichenfolge.

Macht das Sinn?

1

Eine rein funktionalen Ansatz:

module ObjectExtensions = 
type System.Object with 
    member this.BuildString template = 
     let properties = Array.to_list (this.GetType().GetProperties()) 
     let rec updateFromProperties (pps : Reflection.PropertyInfo list) template = 
      if pps = List.Empty then 
       template 
      else 
       let property = List.hd pps 
       let attributes = Array.to_list (property.GetCustomAttributes(typeof<InputRegexAttribute>,true)) 
       let rec updateFromAttributes (ats : obj list) (prop : Reflection.PropertyInfo) (template : string) = 
        if ats = List.Empty then 
         template 
        else 
         let a = (List.hd ats) :?> InputRegexAttribute 
         let regex = new Regex(a.Format) 
         let value = prop.GetValue(this, null).ToString() 
         if regex.IsMatch(value) then 
          template.Replace("{" + prop.Name + "}", value) 
         else 
          raise (new Exception "Regex Failed\n") 
       updateFromProperties(List.tl pps) (updateFromAttributes attributes property template) 
     updateFromProperties properties template