-
23-08-2019 - |
题
大家好,
我最近一直在涉足一些 F#,并想出了以下从一些 C# 代码移植来的字符串生成器。如果它传递属性中定义的正则表达式,它将对象转换为字符串。对于手头的任务来说,这可能有点过分了,但出于学习目的。
目前,BuildString 成员使用可变字符串变量updatedTemplate。我一直在绞尽脑汁想找到一种没有任何可变对象的方法来做到这一点,但没有成功。这让我想到了我的问题。
是否可以在没有任何可变对象的情况下实现 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
解决方案
我觉得你可以随时变换“为遍历序列只有一个效果(突变的局部变量)”成摆脱了可变的代码;这里的一个例子的一般变换:
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 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
(在上面的代码中,我使用的名称“X2”和“×3”,使确定范围变得更加清楚,但是可以只使用名字“X”贯穿。)
这可能是值得尝试做你的示例代码此相同的转换和发布自己的答案。这不一定会产生最惯用代码,但也可以是在转化for循环到Seq.fold呼叫的练习。
(这就是说,在这个例子中整个目标主要是学术活动 - 与所述可变的是“精”的代码)
其他提示
我没有时间将其写成代码,但是:
- 编写一个代表最里面部分的函数,获取一个字符串(到目前为止的结果)以及属性和属性的元组,并返回替换后的字符串。
- 使用
seq.map_concat
从返回的属性数组中获取GetProperties()
到(属性,属性)元组的序列。 - 使用
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
不隶属于 StackOverflow