Question

Gday Tous,

Je suis barboter dans une F # de la fin et je suis venu avec le constructeur de chaîne suivante que je Ported à partir du code C #. Il convertit un objet dans une chaîne de caractères à condition qu'il passe une expression régulière définie dans les attributs. Son probablement trop pour la tâche à accomplir, mais son à des fins d'apprentissage.

Actuellement, le membre BuildString utilise une updatedTemplate variable de chaîne mutable. J'ai creusé la cervelle pour trouver un moyen de faire cela sans aucun objet mutables en vain. Ce qui me amène à ma question.

Est-il possible de mettre en œuvre la fonction de membre BuildString sans objets mutables?

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
Était-ce utile?

La solution

Je pense que vous pouvez toujours transformer une « boucle sur une séquence avec un seul effet (une variable locale muter) » dans le code qui se débarrasse de la mutable; voici un exemple de la transformation générale:

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

Votre exemple particulier a des boucles imbriquées, voici donc un exemple de montrer le même genre de mécanique transformer en deux étapes:

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

(Dans le code ci-dessus, je les noms de x2 'et « x 3 » pour rendre la portée plus apparente, mais vous pouvez simplement utiliser le nom « x » tout au long.)

Il peut être intéressant d'essayer de faire ce même transform sur votre code d'exemple et publiez votre propre réponse. Cela donnera pas nécessairement le code le plus idiomatiques, mais peut être un exercice à transformer une boucle en un appel Seq.fold.

(Cela dit, dans cet exemple, l'ensemble but est essentiellement un exercice académique -. Le code avec le mutable est « bien »)

Autres conseils

Je n'ai pas le temps d'écrire ceci comme code, mais:

  • Ecrire une fonction représentant la partie la plus profonde, en une chaîne (le résultat jusqu'à présent) et un tuple de la propriété et l'attribut, et retourner la chaîne après le remplacement.
  • Utiliser seq.map_concat pour aller de l'éventail de propriétés renvoyées par GetProperties() à une séquence de (propriété, attribut) tuples.
  • Utilisation seq.fold avec les deux bits précédents pour faire toute la transformation, en utilisant le modèle d'origine en tant que valeur initiale pour l'agrégation. Le résultat global sera la chaîne remplacée finale.

Est-ce logique?

Une approche purement fonctionnelle:

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top