Pregunta

Estoy tratando de analizar los argumentos de línea de comando en una aplicación C #. Estoy usando coincidencia de patrones sobre la lista de parámetros para lograrlo. Algo así como:

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

El problema es que quiero hacer caso "/out" partido insensibles preservando al mismo tiempo el caso de otras cosas. Eso significa que no puede alterar la entrada y que coincida con la versión en minúsculas de la entrada en contra de ella (esto perderá la información del caso fileName).

He pensado en varias soluciones:

  • El recurso a cláusulas when que es menos que ideal.
  • coinciden con una tupla cada vez, el primero sería el parámetro real (que voy a guardar para su posterior procesamiento y tendrá que coincidir con Comodín) y la segunda sería la versión en minúsculas utilizado en este tipo de emparejamientos. Esto se ve peor que el primero.
  • Uso de los patrones activos pero que se ve demasiado prolijo. Voy a tener que repetir cosas como ToLower "/out" antes de cada elemento.

¿Hay una mejor opción / patrón para hacer este tipo de cosas? Creo que este es un problema común y no debería ser una buena manera de manejar la situación.

¿Fue útil?

Solución

Me gusta bastante su idea de utilizar patrones activos F # para solucionar esto. Es un poco más detallado que el uso de pre-procesamiento, pero creo que es bastante elegante. También, de acuerdo a algunas pautas BCL , no deberías t estar usando ToLower al comparar cadenas (ignorando el caso). El enfoque correcto es utilizar la bandera OrdinalIgnoreCase. Todavía se puede definir un patrón activo agradable de hacer esto para usted:

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

Tienes razón que es más detallado, pero muy bien oculta la lógica y la que le da el poder suficiente para utilizar el estilo de codificación recomendada (no estoy seguro de cómo esto podría hacerse mediante pre-procesamiento).

Otros consejos

Yo podría hacer un poco de pre-procesamiento para permitir ya sea "-" o "/" al principio de las palabras clave, y para normalizar el caso:

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

Es quizás no es ideal, pero no es como cualquier usuario va a tener bastante paciencia para escribir tantos parámetros de línea de comandos que bucle a través de ellos dos veces es notablemente más lento.

Puede utilizar los guardias para que coincida con su reparto:

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

encontramos con este en busca de una solución a un problema similar, y mientras que la solución de Tomás trabaja para cuerdas individuales, que no ayuda con la emisión original de coincidencia de patrones contra listas de cadenas. Una versión modificada de su patrón activo permite listas de juego:

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"

No he sido capaz de averiguar cómo hacer que coincida con marcadores de posición adecuada para hacer | InvariantEqual "/out" :: fileName :: rest -> ... todavía, pero si usted sabe todo el contenido de la lista, que es una mejora.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top