سؤال

أحاول تحليل وسيطات سطر الأوامر في تطبيق F #. أنا أستخدم نمط مطابقة على قائمة المعلمات لإنجازها. شيء مثل:

let rec parseCmdLnArgs = 
  function
  | [] -> { OutputFile = None ; OtherParam = None }
  | "/out" :: fileName :: rest -> let parsedRest = parseCmdLnArgs rest
                                  { OutputFile = Some(fileName) with parsedRest }

المشكلة أريد أن أجعلها "/out" تطابق حالة غير حساسة مع الحفاظ على حالة الأشياء الأخرى. هذا يعني أنني لا أستطيع تغيير المدخلات ومطابقة الإصدار الصغير من المدخلات ضده (سيخسر ذلك fileName معلومات الحالة).

لقد فكرت في العديد من الحلول:

  • منتجع إلى when البنود التي هي أقل من المثالي.
  • تطابق tuple في كل مرة، الأول سيكون المعلمة الفعلية (التي ستقوم فقط بحفظها لمزيد من المعالجة وسوف تطابق Wildcard IT) والثاني سيكون الإصدار البطلي المستخدم في هذه المطابقات. هذا يبدو أسوأ من الأول.
  • استخدم أنماط نشطة ولكنها تبدو مطهية للغاية. سآخذ لتكرار أشياء مثل ToLower "/out" قبل كل عنصر.

هل هناك خيار / نمط أفضل للقيام بهذا النوع من الأشياء؟ أعتقد أن هذه مشكلة شائعة ويجب أن تكون هناك طريقة جيدة للتعامل معها.

هل كانت مفيدة؟

المحلول

أنا تماما مثل فكرتك عن استخدام أنماط F # النشطة لحل هذا. إنه أمر أكثر قليلا من استخدام المعالجة المسبقة، لكنني أعتقد أنه أنيق للغاية. أيضا، وفقا ل بعض المبادئ التوجيهية BCL., ، يجب أن لا تستخدم ToLower عند مقارنة السلاسل (تجاهل القضية). النهج الصحيح هو استخدام OrdinalIgnoreCase علم. لا يزال بإمكانك تحديد نمط نشط لطيف للقيام بذلك من أجلك:

open System

let (|InvariantEqual|_|) (str:string) arg = 
  if String.Compare(str, arg, StringComparison.OrdinalIgnoreCase) = 0
    then Some() else None

match "HellO" with
| InvariantEqual "hello" -> printfn "yep!"
| _ -> printfn "Nop!"    

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

نصائح أخرى

قد أقوم ببعض المعالجة المسبقة للسماح إما "-" أو "/" في بداية الكلمات الرئيسية، وتطبيع الحالة:

let normalize (arg:string) =
    if arg.[0] = '/' || arg.[0] = '-' then 
        ("-" + arg.[1..].ToLower())
    else arg
let normalized = args |> List.map normalize

ربما لا تكون مثالية، لكنها ليست مثل أي مستخدم سيكون لديه ما يكفي من الصبر لكتابة العديد من معلمات سطر الأوامر التي تحلق من خلالها مرتين بطيئة بشكل ملحوظ.

يمكنك استخدام الحراس لتتناسب مع صفقة الخاص بك:

let rec parseCmdLnArgs = 
  function
  | [] -> { OutputFile = None ; OtherParam = None }
  | root :: fileName :: rest when root.ToUpper() = "/OUT" -> let parsedRest = parseCmdLnArgs rest
                                  { OutputFile = Some(fileName) with parsedRest }

ركض في هذا البحث عن حل لقضية مماثلة، وعلى الرغم من أن حل توماس يعمل من أجل الأوتار الفردية، إلا أنه لا يساعد في القضية الأصلية من مطابقة النمط مقابل قوائم السلاسل. إصدار تعديل من نمطه النشط يسمح بقوائم مطابقة:

let (|InvariantEqual|_|) : string list -> string list -> unit option =
    fun x y ->
        let f : unit option -> string * string -> unit option =
            fun state (x, y) ->
                match state with
                | None -> None
                | Some() ->
                    if x.Equals(y, System.StringComparison.OrdinalIgnoreCase)
                    then Some()
                    else None
        if x.Length <> y.Length then None
        else List.zip x y |> List.fold f (Some())

match ["HeLlO wOrLd"] with
| InvariantEqual ["hello World";"Part Two!"] -> printfn "Bad input"
| InvariantEqual ["hello WORLD"] -> printfn "World says hello"
| _ -> printfn "No match found"

لم أتمكن من معرفة كيفية تطابقها مع العناصر النائبة بشكل صحيح للقيام بها | InvariantEqual "/out" :: fileName :: rest -> ... ومع ذلك، ولكن إذا كنت تعرف كامل محتويات القائمة، فهذا تحسن.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top