سؤال

gday كل شيء،

لقد كنت حذرا في بعض F # من وقت متأخر وتوصلت إلى منشئ السلسلة التالية التي قمت باستمرارها من رمز C #. يقوم بتحويل كائن إلى سلسلة شريطة أن يمر Regex المحدد في السمات. من المحتمل أن تكون مبالغة للمهمة في متناول اليد ولكن لأغراض التعلم.

يستخدم عضو BuilString حاليا وتحديثا سلسلة متغيرة للتغيير. لقد كنت أرفف ذهني للعمل بطريقة تفعل هذا دون أي أشياء قابلة للتغيير دون جدوى. الذي يجلب لي إلى سؤالي.

هل من الممكن تنفيذ وظيفة عضو BuilString دون أي كائنات قابلة للتغيير؟

هتافات،

ميخائيل

//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" و "x3" لإجراء التركيب أكثر وضوحا، ولكن يمكنك فقط استخدام اسم "X" طوال الوقت.)

قد يكون من المفيد محاولة القيام بهذا التحويل نفسه على رمز المثال الخاص بك ونشر إجابتك الخاصة. لن يؤدي هذا بالضرورة إلى أعلى رمز الاصطلاح، ولكن يمكن أن يكون تمرينا في تحويل حلقة إلى مكالمة SEQ.POLD.

(يقال، في هذا المثال، الهدف بأكمله هو في الغالب تمرينا أكاديميا - الرمز مع قابلة للتغيير "جيد".)

نصائح أخرى

ليس لدي الوقت لكتابة هذا الأمر كود، ولكن:

  • اكتب دالة تمثل الجزء الأعمق، مع أخذ سلسلة (النتيجة حتى الآن) و Tuple الخاص بالخاصية والسمة، وإرجاع السلسلة بعد الاستبدال.
  • يستخدم seq.map_concat للذهاب من مجموعة من العقارات التي تعاد GetProperties() إلى تسلسل (الممتلكات، السمة) tuples.
  • يستخدم 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top