Я пытаюсь проанализировать параметры командной строки в приложении F#. Я использую сопоставление с образцом по списку параметров для выполнения его. Что-то как:
let rec parseCmdLnArgs =
function
| [] -> { OutputFile = None ; OtherParam = None }
| "/out" :: fileName :: rest -> let parsedRest = parseCmdLnArgs rest
{ OutputFile = Some(fileName) with parsedRest }
Проблема, я хочу сделать "/out"
соответствие, нечувствительное к регистру при сохранении случая другого материала. Это означает, что я не могу изменить вход и соответствовать строчной версии входа против него (это проиграет fileName
информация о случае).
Я думал о нескольких решениях:
when
пункты, который является меньше, чем идеал. ToLower "/out"
перед каждым объектом. Существует ли более оптимальный вариант / шаблон для того, чтобы сделать подобные материал? Я думаю, что это - типичная проблема и должен быть хороший способ обработать ее.
Мне очень нравится ваше представление о использование активных шаблонов 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!"
Вы правы, он более подробный, но он красиво скрывает логику и дает вам достаточно возможностей для использования рекомендуемого стиля кодирования (я не уверен как это можно сделать с помощью предварительной обработки).
Я мог бы сделать некоторые предварительные обработка, позволяющая использовать «-» или «/» в начале ключевых слов и нормализовать ситуацию:
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 }