Pregunta

Todo Gday,

I se ha metido en alguna F # en los últimos tiempos y se me ocurrió la siguiente constructor de cadena que me portado desde algún código C #. Se convierte un objeto en una cadena siempre y cuando pasa una expresión regular se define en los atributos. Es probablemente una exageración para la tarea en cuestión, pero su con fines de aprendizaje.

Actualmente el miembro BuildString utiliza una variable updatedTemplate cadena mutable. He estado devanando los sesos para encontrar una forma de hacer esto sin ningún tipo de objetos mutables en vano. Lo que me lleva a mi pregunta.

¿Es posible implementar la función de miembro de BuildString sin ningún tipo de objetos mutables?

Saludos,

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
¿Fue útil?

Solución

Creo que siempre se puede transformar un "bucle sobre una secuencia con un solo efecto (mutación de una variable local)" en código que se deshace de lo mutable; he aquí un ejemplo de la transformación general:

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

Su ejemplo particular ha anidado bucles, así que aquí está un ejemplo de mostrar el mismo tipo de mecánica transformar en dos pasos:

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

(En el código anterior, he utilizado los nombres 'x2' y 'x3' para hacer más evidente la determinación del alcance, pero sólo se puede utilizar el nombre de 'X' en todas partes.)

Puede valer la pena tratar de hacer esta misma transformación en el código de ejemplo y publicar su propia respuesta. Esto no necesariamente dió el código más idiomática, pero puede ser un ejercicio en la transformación de un bucle en una llamada Seq.fold.

(Dicho esto, en este ejemplo la meta entera es sobre todo un ejercicio académico -. El código con lo mutable es 'bien')

Otros consejos

No tengo el tiempo para escribir esto como código, pero:

  • Escribir una función que representa la parte más interna, teniendo una cadena (el resultado hasta el momento) y una tupla de la propiedad y el atributo, y devolver la cadena después de la sustitución.
  • Uso seq.map_concat para ir de la serie de propiedades devueltos por GetProperties() a una secuencia de (propiedad, atributo) tuplas.
  • Uso seq.fold con los dos bits anteriores para hacer toda la transformación, utilizando la plantilla original como el valor inicial para la agregación. El resultado global será la cadena reemplazado final.

¿Tiene sentido?

Un enfoque puramente funcional:

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top