Интересные вещи, я думаю, что все, что вы здесь исследуете, действительно.(Частичные) активные шаблоны для сопоставления регулярных выражений работают очень хорошо.Особенно, когда у вас есть строка, которую вы хотите сопоставить с несколькими альтернативными случаями.Единственное, что я хотел бы предложить для более сложных активных шаблонов регулярных выражений, - это дать им более описательные имена, возможно, создав коллекцию различных активных шаблонов регулярных выражений с разными целями.
Что касается вашего примера с C # по 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
, который вы показали (я не отбрасываю первую группу в совпадении, и я возвращаю список объектов Group, а нетолько их значения - один использует параметр скомпилированного регулярного выражения для статических шаблонов регулярных выражений, другой использует интерпретированный параметр регулярного выражения для динамических шаблонов регулярных выражений).Поскольку API .NET regex настолько наполнен функциями, то, что вы возвращаете из своего активного шаблона, действительно соответствует тому, что вы считаете полезным.Но возвращать 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
Обратите также внимание на то, что эти активные шаблоны считают нуль несоответствием, вместо этогобросить исключение.
ОК, так что давайте предположим, что мы хотим разобрать имена.У нас есть следующие требования:
- Должны иметь имя и фамилию
- Может иметь отчество
- Имя, отчество и фамилия отделены однимпробел в указанном порядке
- Каждая часть имени может состоять из любой комбинации по крайней мере одной или нескольких букв или цифр
- Ввод может быть искажен
Первыймы определим следующую запись:
type Name = {First:string; Middle:option<string>; Last:string}
Тогда мы сможем довольно эффективно использовать наш активный шаблон регулярных выражений в функции для анализа имени:
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
Обратите внимание на одно из ключевых преимуществмы получаем здесь, что в случае с сопоставлением с образцом в целом, то, что мы можем одновременно проверить, что вход соответствует шаблону регулярных выражений, и разложить возвращенный список групп, если это так.