簡體   English   中英

F#可變為不可變

[英]F# Mutable to Immutable

Gday All,

我一直在涉及一些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

一種純功能方法:

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

我沒有時間把它寫成代碼,但是:

  • 編寫一個表示最里面部分的函數,取一個字符串(到目前為止的結果)和屬性和屬性的元組,並在替換后返回字符串。
  • 使用seq.map_concatGetProperties()返回的屬性數組轉到(屬性,屬性)元組序列。
  • 使用seq.fold和前兩位進行整個轉換,使用原始模板作為聚合的初始值。 總體結果將是最終替換的字符串。

那有意義嗎?

我認為你總是可以將“只有一個效果的序列for 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 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'和'x3'來使范圍更明顯,但你可以在整個過程中使用名稱'x'。)

嘗試對示例代碼執行相同的轉換並發布您自己的答案可能是值得的。 這不一定會產生最慣用的代碼,但可以是將for循環轉換為Seq.fold調用的練習。

(也就是說,在這個例子中,整個目標主要是學術練習 - 具有可變性的代碼是'很好'。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM