F # Mutable zu Immutable
-
23-08-2019 - |
Frage
Gday Alle,
Ich habe in einigem F # in der letzten Zeit Dilettantismus und ich kam mit den folgenden String-Builder, die ich von einigem C # -Code portierte. Er wandelt ein Objekt in einen String, sofern sie eine Regex in den Attributen definiert gibt. Es ist wahrscheinlich übertrieben für die Aufgabe zur Hand, aber sein zu Lernzwecken.
Derzeit ist das Build Mitglied nutzt einen veränderlichen String-Variable updatedTemplate. Ich habe mein Gehirn wurde Abstich einen Weg dies zu tun arbeiten, ohne veränderbare Objekte ohne Erfolg. Das bringt mich auf meine Frage.
Ist es möglich, die Buildelementfunktion 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
Lösung
Ich denke, man kann immer „mit nur einem Effekt (mutiert eine lokale Variable) für Schleife über eine Sequenz“ in den Code einer Transformation, 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 hat Schleifen verschachtelt, also hier ist ein Beispiel für die gleiche Art von mechanischen zeigen in zwei Schritten umwandeln:
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 machen Scoping, aber Sie können nur den Namen verwenden ‚x‘ im gesamten Gebäude.)
Es kann sinnvoll sein, um zu versuchen, dieselbe zu tun, auf dem Beispiel-Code umwandeln und Ihre eigene Antwort posten. Dies wird nicht unbedingt den meisten idiomatischen Code ergibt, kann aber für Schleife in einen Seq.fold Anruf bei der Umwandlung eine Übung sein.
(Das heißt, in diesem Beispiel das ganze Ziel ist meist eine akademische Übung -. Der Code mit der wandelbar ist ‚fein‘)
Andere Tipps
Ich habe nicht die Zeit, dies als Code zu schreiben, aber:
- Schreiben Sie eine Funktion, die innerste Teil darstellt, eine Zeichenfolge (das Ergebnis bisher) und ein Tupel der Immobilie und das Attribut nehmen und die Zeichenfolge nach dem Austausch zurück.
- Verwenden Sie
seq.map_concat
gehen aus der Reihe von Eigenschaften vonGetProperties()
in eine Folge von (Eigenschaft, ein Attribut) Tupel zurückgegeben. - Verwenden Sie
seq.fold
mit den vorherigen zwei Bits, die die gesamte Transformation zu tun, die ursprüngliche Vorlage als Ausgangswert für die Aggregation verwendet wird. Das Gesamtergebnis wird die endgültige Fassung String sein.
Ist das sinnvoll?
Ein rein funktionaler 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