نمط حالة غير حساسة مطابقة على قوائم السلسلة
-
19-09-2019 - |
سؤال
أحاول تحليل وسيطات سطر الأوامر في تطبيق 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 -> ...
ومع ذلك، ولكن إذا كنت تعرف كامل محتويات القائمة، فهذا تحسن.