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