Frage

Ich versuche, Befehlszeilenargumente in einer F # Anwendung zu analysieren. Ich verwende Musterliste passend über Parameter um es zu erreichen. So etwas wie:

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

Das Problem ist, ich möchte "/out" Spiel Groß- und Kleinschreibung unter Beibehaltung der bei anderen Sachen machen. Das heißt, ich kann nicht die Eingabe ändern und die Kleine Version des Eingangs dagegen Übereinstimmen (dies wird die fileName Fall Informationen verlieren).

Ich habe darüber nachgedacht, mehrere Lösungen:

  • Resort when Klauseln, die weniger als ideal ist.
  • passt jedes Mal ein Tupel, der erste, der aktuelle Parameter würde (was ich für die weitere Verarbeitung speichern nur und wird es Wildcard entsprechen) und die zweite wäre die kleingeschrieben Version in solchen Matchings verwendet werden. Das sieht schlimmer als die erste.
  • Verwenden Sie aktive Muster, aber das sieht zu ausführlich. Ich werde Dinge wie ToLower "/out" wiederholen müssen vor jedem Punkt.

Gibt es eine bessere Option / Muster diese Art von Sachen zu tun? Ich denke, dies ist ein häufiges Problem und es sollte ein guter Weg, um es zu behandeln.

War es hilfreich?

Lösung

Ich mag Ihre Idee der Verwendung von F # aktiven Muster, diese zu lösen. Es ist ein wenig ausführlicher als Pre-Processing, aber ich denke, es ist ziemlich elegant. Auch nach einige BCL Richtlinien , Sie shouldn‘ t werden mit ToLower wenn Strings zu vergleichen (den Fall zu ignorieren). Der richtige Ansatz ist OrdinalIgnoreCase Flagge zu verwenden. Sie können immer noch ein schönes aktives Muster definieren, dies für Sie tun:

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

Sie haben Recht, dass es ausführlicher, aber es verbirgt sich schön die Logik und es gibt Ihnen genügend Energie die empfohlene Codierung Stil zu verwenden (Ich bin nicht sicher, wie diese Vorverarbeitung getan werden könnte, verwendet wird).

Andere Tipps

Ich könnte einige Vorverarbeitung tun für entweder zu ermöglichen „-“ oder „/“ am Anfang Keywords und den Fall zu normalisieren:

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

Es ist vielleicht nicht ideal, aber es ist nicht wie jeder Benutzer genügend Geduld haben wird so viele Befehlszeilenparameter eingeben, die durch sie zweimal Looping ist merklich langsam.

Sie können Wachen benutzen Sie Ihr Angebot entsprechen:

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

Ran diese in eine Lösung für ein ähnliches Problem suchen, und während Tomas' Lösung für die einzelnen Strings arbeitet, ist es nicht mit dem Original-Ausgabe von Mustervergleich gegen Listen von Zeichenketten helfen. Eine modifizierte Version seiner aktiven Struktur ermöglicht Abgleichungslisten:

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"

Ich habe nicht in der Lage gewesen, um herauszufinden, wie es mit Platzhalter machen paßt richtig | InvariantEqual "/out" :: fileName :: rest -> ... noch zu tun, aber wenn Sie den gesamten Inhalt der Liste wissen, es ist eine Verbesserung.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top