Domanda

Tutti Gday,

Sono stato dilettarsi in qualche F # di ritardo e mi si avvicinò con la seguente stringa builder che ho portato da qualche codice C #. Si converte un oggetto in una stringa condizione che passa un Regex definito negli attributi. La sua probabilmente eccessivo per il compito a portata di mano, ma la sua a fini di apprendimento.

Al momento il membro BuildString utilizza una stringa mutabile updatedTemplate variabile. Ho tormentando il mio cervello per trovare un modo di fare questo senza oggetti mutabili senza alcun risultato. Il che mi porta alla mia domanda.

E 'possibile implementare la funzione di membro BuildString senza oggetti mutabili?

Saluti,

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
È stato utile?

Soluzione

Penso che si può sempre trasformare un "ciclo for su una sequenza con un solo effetto (mutando una variabile locale)" in codice che si sbarazza del mutabile; ecco un esempio del generale di trasformare:

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

Il tuo esempio particolare è annidato loop, ecco un esempio di mostrare lo stesso tipo di meccanica trasformare in due fasi:

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

(Nel codice di cui sopra, ho usato i nomi 'x2' e 'x3' per rendere scoping più evidente, ma si può semplicemente utilizzare il nome di 'X' in tutto.)

Può essere utile per cercare di fare questa stessa trasformazione su vostro codice di esempio e comunica la tua propria risposta. Questo non sarà necessariamente cedere il codice più idiomatica, ma può essere un esercizio di trasformare un ciclo in una chiamata Seq.fold.

(Detto questo, in questo esempio, l'intero obiettivo è per lo più un esercizio accademico -. Il codice con il mutevole è 'bene')

Altri suggerimenti

Non ho il tempo per scrivere questo come codice, ma:

  • scriva una funzione che rappresenta la parte più interna, prendendo una stringa (il risultato finora) e una tupla della proprietà e l'attributo, e restituendo la stringa dopo la sostituzione.
  • Usa seq.map_concat per passare dalla matrice di proprietà restituite da GetProperties() ad una sequenza di (immobili, attributo) tuple.
  • Usa seq.fold con i precedenti due bit per fare l'intero trasformazione, utilizzando il modello originale come valore iniziale per l'aggregazione. Il risultato complessivo sarà la stringa di sostituzione finale.

Ha senso?

Un approccio puramente funzionale:

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top