2010-02-08 14 views
17

मैं एक एफ # अनुप्रयोग में कमांड लाइन तर्कों का विश्लेषण करने की कोशिश कर रहा हूं। मैं इसे पूरा करने के लिए पैरामीटर सूची से मिलान पैटर्न का उपयोग कर रहा हूँ। की तरह कुछ:स्ट्रिंग सूचियों पर केस असंवेदनशील पैटर्न मिलान

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

समस्या मैं जबकि अन्य सामान के मामले संरक्षण "/out" मैच केस संवेदी बनाना चाहते है। इसका मतलब है कि मैं इनपुट को बदल नहीं सकता और इसके खिलाफ इनपुट के लोअरकेस संस्करण से मेल नहीं खाता (इससे fileName केस जानकारी खो जाएगी)।

मैं कई समाधान के बारे में सोचा है:

  • रिज़ॉर्ट when को खंड जो आदर्श से कम है।
  • प्रत्येक बार एक टुपल से मेल खाता है, पहला वास्तविक पैरामीटर होगा (जो मैं अभी आगे प्रसंस्करण के लिए सहेजूंगा और वाइल्डकार्ड मैच करेगा) और दूसरा इस तरह के मिलान में उपयोग किया जाने वाला निम्न संस्करण होगा। यह पहले से भी बदतर लग रहा है।
  • सक्रिय पैटर्न का उपयोग करें लेकिन यह बहुत वर्बोज़ दिखता है। मुझे प्रत्येक आइटम से पहले ToLower "/out" जैसी चीजों को दोहराना होगा।

क्या इस तरह की चीजें करने के लिए कोई बेहतर विकल्प/पैटर्न है? मुझे लगता है कि यह एक आम समस्या है और इसे संभालने का एक अच्छा तरीका होना चाहिए।

उत्तर

27

मैं काफी यह हल करने के लिए एफ # सक्रिय पैटर्न का उपयोग कर के अपने विचार की तरह। प्री-प्रोसेसिंग का उपयोग करने से यह थोड़ा और वर्बोज़ है, लेकिन मुझे लगता है कि यह काफी सुरुचिपूर्ण है। इसके अलावा, some BCL guidelines के अनुसार, स्ट्रिंग की तुलना करते समय आपको 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!"  

तुम सही है कि यह अधिक वर्बोज़ है, लेकिन यह अच्छी तरह से तर्क छुपाता है और यह आप की सिफारिश की कोडिंग शैली का उपयोग करने के लिए पर्याप्त शक्ति (मैं देता मुझे यकीन नहीं है कि प्री-प्रोसेसिंग का उपयोग करके यह कैसे किया जा सकता है)।

+1

मुझे 'InvarianinalEqual' नाम गुमराह करने के लिए नाम मिलता है क्योंकि आप' ऑर्डिनल इग्नोरकेस 'का उपयोग नहीं कर रहे हैं' InvariantCultureIgnoreCase' – Maslow

2

मैं या तो के लिए अनुमति देने के लिए कुछ पूर्व प्रसंस्करण कर सकता "-" या "/" कीवर्ड की शुरुआत में, और मामले को सामान्य बनाने में:

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

यह शायद आदर्श नहीं है, लेकिन यह नहीं है किसी भी उपयोगकर्ता को इतने सारे कमांड लाइन पैरामीटर टाइप करने के लिए पर्याप्त धैर्य रखने वाला है जो दो बार उनके माध्यम से लूपिंग काफी धीमा है।

2

आप अपने सौदा मैच के लिए गार्ड का उपयोग कर सकते हैं:

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

इस तरह के एक समान मुद्दे के समाधान की तलाश में दौड़ें, और जब टॉमस का समाधान अलग-अलग तारों के लिए काम करता है, तो यह तारों की सूचियों के खिलाफ पैटर्न पैटर्न के मूल मुद्दे में मदद नहीं करता है। अपनी सक्रिय पैटर्न की एक संशोधित संस्करण मिलान सूचियों की अनुमति देता है:

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 -> ... करने के लिए बनाने के लिए करने में सक्षम नहीं किया गया, लेकिन यदि आप सूची की सारी सामग्री को पता है , यह एक सुधार है।

संबंधित मुद्दे