Case Insensitive Pattern Matching über String-Listen
-
19-09-2019 - |
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.
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.