2009-05-21 46 views
4

GDAY所有,F#可變的,以永恆

我一直在涉足較晚一些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 

回答

1

我認爲你總是可以將只有一個效果(變異局部變量)的「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調用。

(也就是說,在這個例子中,整個目標主要是一個學術活動 - 與可變代碼是「精」。)

+0

乾杯布賴恩和Jon 。我會執行這個摺疊,看看它是如何發生的。就像我在問題中提到的那樣,這純粹是一種學術活動。 – Michael 2009-05-21 22:43:19

1

我沒有把這件事寫成代碼的時間,但是:

  • 寫代表最深處的功能,服用字符串(到目前爲止的結果)以及屬性和屬性的元組,並在替換後返回字符串。
  • 使用seq.map_concat將由GetProperties()返回的屬性數組轉換爲(屬性,屬性)元組序列。
  • 使用seq.fold與前兩位進行整個轉換,使用原始模板作爲聚合的初始值。總體結果將是最終替換的字符串。

這有道理嗎?

1

純功能的方法:

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