Ответ 1
Интересный материал, я думаю, что все, что вы здесь изучаете, действительно. (Частичные) активные шаблоны для регулярного сравнения выражений очень хорошо работают. Особенно, когда у вас есть строка, которую вы хотите сопоставить с несколькими альтернативными случаями. Единственное, что я предлагаю с более сложными активными шаблонами регулярных выражений, это то, что вы даете им более описательные имена, возможно, создавая коллекцию различных активных шаблонов регулярных выражений с различными целями.
Что касается вашего примера с С# на F #, вы можете иметь функциональное решение просто отлично без активных шаблонов, например.
let testString = "http://www.bob.com http://www.b.com http://www.bob.com http://www.bill.com"
let matches input =
Regex.Matches(input, "(http:\/\/\S+)")
|> Seq.cast<Match>
|> Seq.groupBy (fun m -> m.Value)
|> Seq.map (fun (value, groups) -> value, (groups |> Seq.length))
//FSI output:
> matches testString;;
val it : seq<string * int> =
seq
[("http://www.bob.com", 2); ("http://www.b.com", 1);
("http://www.bill.com", 1)]
Обновление
Причина, почему этот конкретный пример отлично работает без активных шаблонов, состоит в том, что: 1) вы только тестируете один шаблон, 2) вы динамически обрабатываете совпадения.
Для примера реальных активных шаблонов рассмотрим случай, когда 1) мы тестируем несколько регулярных выражений, 2) мы тестируем одно соответствие регулярных выражений с несколькими группами. Для этих сценариев я использую следующие два активных шаблона, которые немного более общие, чем первый активный шаблон Match
, который вы показали (я не отбрасываю первую группу в матче, и я возвращаю список объектов группы, а не просто их значения - используется скомпилированная опция регулярного выражения для статических шаблонов регулярных выражений, в которой используется интерпретируемая опция регулярного выражения для динамических шаблонов регулярных выражений). Поскольку API регулярных выражений .NET настолько заполнен, что вы возвращаетесь из активного шаблона, действительно зависит от того, что вы считаете полезным. Но возвращение list
чего-то хорошее, потому что тогда вы можете сопоставить шаблон в этом списке.
let (|InterpretedMatch|_|) pattern input =
if input = null then None
else
let m = Regex.Match(input, pattern)
if m.Success then Some [for x in m.Groups -> x]
else None
///Match the pattern using a cached compiled Regex
let (|CompiledMatch|_|) pattern input =
if input = null then None
else
let m = Regex.Match(input, pattern, RegexOptions.Compiled)
if m.Success then Some [for x in m.Groups -> x]
else None
Обратите внимание также, как эти активные шаблоны рассматривают null как несоответствие, вместо того, чтобы бросать исключение.
ОК, так что скажем, мы хотим разобрать имена. У нас есть следующие требования:
- Должно иметь имя и фамилию
- Может иметь среднее имя
- Во-первых, необязательное среднее и фамильное имя разделяются одним пробелом в этом порядке
- Каждая часть имени может состоять из любой комбинации по меньшей мере одной или нескольких букв или цифр.
- Ввод может быть неверным
Сначала мы определим следующую запись:
type Name = {First:string; Middle:option<string>; Last:string}
Затем мы можем эффективно использовать наш активный шаблон regex в функции для разбора имени:
let parseName name =
match name with
| CompiledMatch @"^(\w+) (\w+) (\w+)$" [_; first; middle; last] ->
Some({First=first.Value; Middle=Some(middle.Value); Last=last.Value})
| CompiledMatch @"^(\w+) (\w+)$" [_; first; last] ->
Some({First=first.Value; Middle=None; Last=last.Value})
| _ ->
None
Обратите внимание на одно из ключевых преимуществ, которые мы получаем здесь, что имеет место с совпадением шаблонов в целом, заключается в том, что мы можем одновременно проверить, что вход соответствует шаблону регулярного выражения, и разложите возвращенный список групп, если это произойдет.