18

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"すべてのアイテムの前のように繰り返す必要があります。

この種のことを行うためのより良いオプション/パターンはありますか? これは一般的な問題であり、それを処理する良い方法があるはずだと思います。

4

4 に答える 4

32

これを解決するために F# アクティブ パターンを使用するというあなたのアイデアがとても気に入っています。前処理を使用するよりも少し冗長ですが、非常にエレガントだと思います。また、一部の BCL ガイドラインによれば、文字列を比較するときに使用しないでください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!"    

より冗長であることは間違いありませんが、ロジックがうまく隠され、推奨されるコーディング スタイルを使用するのに十分な力が得られます (前処理を使用してこれを行う方法がわかりません)。

于 2010-02-09T02:34:57.047 に答える
2

キーワードの先頭に「-」または「/」のいずれかを許可し、大文字と小文字を正規化するために、前処理を行う場合があります。

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

おそらく理想的ではありませんが、コマンドラインパラメータを2回ループするのが著しく遅いほど多くのコマンドラインパラメータを入力するのに十分な忍耐力を持っているユーザーはいないでしょう。

于 2010-02-08T22:51:42.067 に答える
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 }
于 2010-02-09T02:32:02.183 に答える
1

同様の問題の解決策を探してこれに遭遇しました.Tomasの解決策は個々の文字列に対して機能しますが、文字列のリストに対するパターンマッチングの元の問題には役立ちません. 彼のアクティブなパターンの修正版では、リストの一致が可能です。

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 -> ...まだ理解できていませんが、リストの内容全体を知っていれば改善されます.

于 2015-03-11T16:09:25.477 に答える