2010-02-08 35 views
17

Tôi đang cố phân tích đối số dòng lệnh trong ứng dụng F #. Tôi đang sử dụng mô hình phù hợp với các danh sách tham số để thực hiện nó. Một cái gì đó như:Khớp mẫu không phân biệt chữ hoa chữ thường trên các danh sách chuỗi

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

Vấn đề là tôi muốn làm "/out" không phân biệt chữ hoa chữ thường trong khi vẫn giữ nguyên các thứ khác. Điều đó có nghĩa là tôi không thể thay đổi đầu vào và khớp với phiên bản chữ thường của đầu vào vào nó (điều này sẽ mất thông tin trường hợp fileName).

Tôi đã nghĩ về một số giải pháp:

  • Resort tới when khoản đó là ít hơn lý tưởng.
  • Khớp một tuple mỗi lần, tham số đầu tiên sẽ là tham số thực (mà tôi sẽ lưu để xử lý tiếp và ký tự đại diện khớp với nó) và thứ hai sẽ là phiên bản được sử dụng trong các kết quả phù hợp. Điều này có vẻ tồi tệ hơn lần đầu tiên.
  • Sử dụng các mẫu hoạt động nhưng có vẻ quá chi tiết. Tôi sẽ phải lặp lại những thứ như ToLower "/out" trước mỗi mục.

Có tùy chọn/mẫu tốt hơn để thực hiện các loại nội dung này không? Tôi nghĩ rằng đây là một vấn đề phổ biến và phải có một cách tốt để xử lý nó.

Trả lời

27

tôi rất thích ý tưởng của bạn sử dụng F # mẫu tích cực để giải quyết việc này. Đó là một chút tiết hơn so với sử dụng tiền xử lý, nhưng tôi nghĩ rằng nó khá thanh lịch. Ngoài ra, theo số some BCL guidelines, bạn không nên sử dụng ToLower khi so sánh chuỗi (bỏ qua trường hợp). Cách tiếp cận đúng là sử dụng cờ OrdinalIgnoreCase. Bạn vẫn có thể xác định một mô hình hoạt động tốt đẹp để làm điều này cho bạn:

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!"  

Bạn nói đúng rằng nó tiết hơn, nhưng nó độc đáo ẩn logic và nó mang lại cho bạn đủ năng lượng để sử dụng phong cách mã hóa được đề xuất (I 'không chắc chắn làm thế nào điều này có thể được thực hiện bằng cách sử dụng tiền xử lý).

+1

Tôi tìm thấy tên 'InvariantEqual' là gây hiểu nhầm vì bạn đang sử dụng' OrdinalIgnoreCase' không phải 'InvariantCultureIgnoreCase' – Maslow

2

tôi có thể làm một số tiền xử lý để cho phép một trong hai "-" hoặc "/" ở đầu của các từ khóa, và bình thường hóa các trường hợp:

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

Đó có lẽ không phải lý tưởng, nhưng nó không giống như bất kỳ người dùng nào cũng sẽ có đủ kiên nhẫn để nhập rất nhiều tham số dòng lệnh lặp qua chúng hai lần đáng chú ý là chậm.

2

Bạn có thể sử dụng vệ sĩ để phù hợp với thỏa thuận của bạn:

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

Bắt đầu tìm kiếm giải pháp cho vấn đề tương tự và giải pháp Tomas hoạt động cho các chuỗi riêng lẻ, nó không giúp giải quyết vấn đề ban đầu của mẫu khớp với danh sách chuỗi. Một phiên bản sửa đổi của mô hình hoạt động của mình cho phép danh sách phù hợp:

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" 

tôi đã không thể tìm ra cách để làm cho nó phù hợp với placeholders đúng cách để làm | InvariantEqual "/out" :: fileName :: rest -> ... nêu ra, nhưng nếu bạn biết toàn bộ nội dung của danh sách , đó là một sự cải tiến.

Các vấn đề liên quan