Поиск…
Простые активные шаблоны
Активные модели представляют собой особый тип сопоставления с образцом , где вы можете указать , названные категории , что ваши данные могут попасть в, а затем использовать эти категории в match
отчетности.
Чтобы определить активный шаблон, который классифицирует числа как положительные, отрицательные или нулевые:
let (|Positive|Negative|Zero|) num =
if num > 0 then Positive
elif num < 0 then Negative
else Zero
Затем это можно использовать в выражении соответствия шаблону:
let Sign value =
match value with
| Positive -> printf "%d is positive" value
| Negative -> printf "%d is negative" value
| Zero -> printf "The value is zero"
Sign -19 // -19 is negative
Sign 2 // 2 is positive
Sign 0 // The value is zero
Активные шаблоны с параметрами
Активные шаблоны - это просто простые функции.
Подобно функциям вы можете определить дополнительные параметры:
let (|HasExtension|_|) expected (uri : string) =
let result = uri.EndsWith (expected, StringComparison.CurrentCultureIgnoreCase)
match result with
| true -> Some true
| _ -> None
Это можно использовать в шаблоне, соответствующем этому способу:
let isXMLFile uri =
match uri with
| HasExtension ".xml" _ -> true
| _ -> false
Активные шаблоны могут использоваться для проверки и преобразования аргументов функции
Интересное, но довольно неизвестное использование активных шаблонов в F#
заключается в том, что они могут использоваться для проверки и преобразования аргументов функции.
Рассмотрим классический способ проверки правильности аргументов:
// val f : string option -> string option -> string
let f v u =
let v = defaultArg v "Hello"
let u = defaultArg u "There"
v + " " + u
// val g : 'T -> 'T (requires 'T null)
let g v =
match v with
| null -> raise (System.NullReferenceException ())
| _ -> v.ToString ()
Обычно мы добавляем код в метод, чтобы проверить правильность аргументов. Используя Active Patterns в F#
мы можем обобщить это и объявить намерение в объявлении аргумента.
Следующий код эквивалентен приведенному выше коду:
let inline (|DefaultArg|) dv ov = defaultArg ov dv
let inline (|NotNull|) v =
match v with
| null -> raise (System.NullReferenceException ())
| _ -> v
// val f : string option -> string option -> string
let f (DefaultArg "Hello" v) (DefaultArg "There" u) = v + " " + u
// val g : 'T -> string (requires 'T null)
let g (NotNull v) = v.ToString ()
Для пользователя функций f
и g
нет разницы между двумя разными версиями.
printfn "%A" <| f (Some "Test") None // Prints "Test There"
printfn "%A" <| g "Test" // Prints "Test"
printfn "%A" <| g null // Will throw
Вызывает беспокойство, если Active Patterns увеличивает накладные расходы. Давайте используем ILSpy
для декомпиляции f
и g
чтобы убедиться, что это так.
public static string f(FSharpOption<string> _arg2, FSharpOption<string> _arg1)
{
return Operators.DefaultArg<string>(_arg2, "Hello") + " " + Operators.DefaultArg<string>(_arg1, "There");
}
public static string g<a>(a _arg1) where a : class
{
if (_arg1 != null)
{
a a = _arg1;
return a.ToString();
}
throw new NullReferenceException();
}
Благодаря inline
Active Patterns добавляет лишних накладных расходов по сравнению с классическим способом проверки правильности аргументов.
Активные шаблоны как обертки .NET API
Активные шаблоны могут использоваться для того, чтобы сделать вызов некоторого .NET API более естественным, особенно те, которые используют выходной параметр, чтобы возвращать больше, чем просто возвращаемое значение функции.
Например, вы обычно вызываете метод System.Int32.TryParse
следующим образом:
let couldParse, parsedInt = System.Int32.TryParse("1")
if couldParse then printfn "Successfully parsed int: %i" parsedInt
else printfn "Could not parse int"
Вы можете немного улучшить это, используя сопоставление шаблонов:
match System.Int32.TryParse("1") with
| (true, parsedInt) -> printfn "Successfully parsed int: %i" parsedInt
| (false, _) -> printfn "Could not parse int"
Однако мы также можем определить следующий активный шаблон, который обертывает функцию System.Int32.TryParse
:
let (|Int|_|) str =
match System.Int32.TryParse(str) with
| (true, parsedInt) -> Some parsedInt
| _ -> None
Теперь мы можем сделать следующее:
match "1" with
| Int parsedInt -> printfn "Successfully parsed int: %i" parsedInt
| _ -> printfn "Could not parse int"
Другим хорошим кандидатом для включения в активные шаблоны являются API регулярных выражений:
let (|MatchRegex|_|) pattern input =
let m = System.Text.RegularExpressions.Regex.Match(input, pattern)
if m.Success then Some m.Groups.[1].Value
else None
match "bad" with
| MatchRegex "(good|great)" mood ->
printfn "Wow, it's a %s day!" mood
| MatchRegex "(bad|terrible)" mood ->
printfn "Unfortunately, it's a %s day." mood
| _ ->
printfn "Just a normal day"
Полные и частичные активные шаблоны
Существует два типа активных шаблонов, которые несколько отличаются в использовании - Полный и Частичный.
Полные активные шаблоны можно использовать, когда вы можете перечислить все результаты, например «число нечетное или даже?».
let (|Odd|Even|) number =
if number % 2 = 0
then Even
else Odd
Обратите внимание, что определение активного шаблона содержит список возможных случаев и ничего другого, и тело возвращает один из перечисленных случаев. Когда вы используете его в выражении соответствия, это полное совпадение:
let n = 13
match n with
| Odd -> printf "%i is odd" n
| Even -> printf "%i is even" n
Это удобно, если вы хотите разбить входное пространство на известные категории, которые полностью его покрывают.
С другой стороны, частичные активные шаблоны позволяют явно игнорировать некоторые возможные результаты, возвращая option
. В их определении используется частный случай _
для непревзойденного случая.
let (|Integer|_|) str =
match Int32.TryParse(str) with
| (true, i) -> Some i
| _ -> None
Таким образом, мы можем сопоставлять, даже если некоторые случаи не могут быть обработаны нашей функцией синтаксического анализа.
let s = "13"
match s with
| Integer i -> "%i was successfully parsed!" i
| _ -> "%s is not an int" s
Частичные активные шаблоны могут использоваться как форма тестирования, независимо от того, попадает ли вход в определенную категорию во входное пространство, игнорируя другие параметры.