文字列リストに対する大文字と小文字を区別しないパターン マッチング
-
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
理想とは程遠い条項。 - 毎回タプルを照合します。最初のパラメーターは実際のパラメーター (今後の処理のために保存し、ワイルドカードで照合します) になり、2 番目はそのような照合で使用される小文字バージョンになります。これは最初のものよりも悪く見えます。
- アクティブなパターンを使用しますが、冗長すぎます。のようなことを繰り返さなければなりません
ToLower "/out"
すべての項目の前に。
このようなことを行うためのより良いオプション/パターンはありますか?これはよくある問題であり、適切な対処方法があるはずだと思います。
解決
私はかなりこれを解決するためのF#アクティブなパターンを使用してのアイデアのように。これは、もう少し詳細な前処理を使用するよりも、私はそれが非常にエレガントだと思います。 に応じていくつかのBCLガイドラインまた、あなたが「shouldn (ケースを無視して)文字列を比較するときtは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 -> ...
を行うために、適切にプレースホルダと一致させる方法を見つけ出すことができていないが、あなたは、リストの内容全体を知っていれば、それは改善だ。