Reflection F#可变到不可变

Reflection F#可变到不可变,reflection,f#,immutability,mutable,Reflection,F#,Immutability,Mutable,好的 我最近一直在涉猎一些F#的东西,我从一些C#代码中移植了以下字符串生成器。它将对象转换为字符串,前提是它传递属性中定义的正则表达式。对于手头的任务来说,这可能是过火了,但对于学习来说却是如此 当前BuildString成员使用可变字符串变量UpdateTemplate。我一直在绞尽脑汁想办法在没有任何可变对象的情况下做到这一点,但毫无效果。这就引出了我的问题 是否可以在没有任何可变对象的情况下实现BuildString成员函数 干杯 迈克尔 //The Validation Attribu

好的

我最近一直在涉猎一些F#的东西,我从一些C#代码中移植了以下字符串生成器。它将对象转换为字符串,前提是它传递属性中定义的正则表达式。对于手头的任务来说,这可能是过火了,但对于学习来说却是如此

当前BuildString成员使用可变字符串变量UpdateTemplate。我一直在绞尽脑汁想办法在没有任何可变对象的情况下做到这一点,但毫无效果。这就引出了我的问题

是否可以在没有任何可变对象的情况下实现BuildString成员函数

干杯

迈克尔

//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
//验证属性
键入public InputRegexAttribute public(格式:string),如下所示=
继承属性()
成员self.Format with get()=Format
//类定义
键入public Foo public(firstName,familyName)如下=
[]
get()=FirstName的成员self.FirstName
[]
get()=FamilyName的成员self.FamilyName
模块对象扩展=
使用
使用此.BuildString模板创建成员=
让可变的UpdateTemplate:string=template
对于.GetType().GetProperties()中的prop
对于prop.GetCustomAttributes中的属性(typeof,true).Cast()do
让regex=newregex(attribute.Format)
让value=prop.GetValue(this,null).ToString()
如果regex.IsMatch(值),则
已更新模板打印文件“%s”e.消息

我没有时间把它写成代码,但是:

  • 编写一个表示最里面部分的函数,获取字符串(到目前为止的结果)、属性和属性的元组,并在替换后返回字符串
  • 使用
    seq.map\u concat
    GetProperties()
    返回的属性数组转到(属性,属性)元组序列
  • 使用前两位的
    seq.fold
    执行整个转换,使用原始模板作为聚合的初始值。整体结果将是最终替换的字符串

这有意义吗?

我认为您总是可以将“序列上只有一种效果的for循环(变异局部变量)”转换为摆脱可变变量的代码;下面是一般变换的一个示例:

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
let inputSeq=[1;2;3]
//原始可变
设可变x=“”
对于输入序列do中的n
设nStr=n.ToString()
x序列折叠(乐趣x n->
//“循环”主体,返回
//新值,而不是更新可变值
设nStr=n.ToString()
x+nStr
)“”//初始值
printfn“结果:“%s”结果
您的特定示例具有嵌套循环,因此下面是一个通过两个步骤显示相同类型机械变换的示例:

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
让inputSeq1=[1;2;3]
让inputSeq2=[“A”;“B”]
让原版()
设可变x=“”
对于输入序列1中的n,请执行以下操作
对于输入Seq2 do中的s
设nStr=n.ToString()
x
设nStr=n.ToString()
x2+nStr+s
)x
printfn“结果:'%s'”x
让下一个Transfer MouterLoopToFold()=
让结果=
inputSeq1 |>Seq.fold(趣味x3 n->
inputSeq2 |>Seq.fold(趣味x2 s->
设nStr=n.ToString()
x2+nStr+s
)x3
) ""
printfn“结果:“%s”结果
(在上面的代码中,我使用了名称'x2'和'x3'使作用域更加明显,但您可以在整个代码中使用名称'x'

尝试对示例代码进行同样的转换并发布自己的答案可能是值得的。这不一定会产生最惯用的代码,但可以作为将for循环转换为Seq.fold调用的练习


(也就是说,在这个例子中,整个目标主要是一个学术练习-带有可变项的代码是“好的”。

一个纯粹的函数方法:

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
模块对象扩展=
使用
使用此.BuildString模板创建成员=
让properties=Array.to_列表(this.GetType().GetProperties())
让rec更新复合属性(pps:Reflection.PropertyInfo列表)模板=
如果pps=List.Empty,则
模板
其他的
let property=List.hd pps
让attributes=Array.to_列表(property.GetCustomAttributes(typeof,true))
let rec updateFromAttribute(ats:obj列表)(prop:Reflection.PropertyInfo)(模板:字符串)=
如果ats=列表,则为空
模板
其他的
设a=(List.hd ats):?>InputRegexAttribute
设regex=newregex(a.Format)
让value=prop.GetValue(this,null).ToString()
如果regex.IsMatch(值),则
替换(“{”+prop.Name+“}”,值)
其他的
raise(新异常“Regex失败\n”)
UpdateFromProperty(List.tl pps)(updateFromAttributes属性模板)
UpdateProperty属性模板

为布赖恩和乔恩干杯。我将实施折叠,看看结果如何。正如我在问题中提到的,这纯粹是一项学术活动。