Поиск…


Простые активные шаблоны

Активные модели представляют собой особый тип сопоставления с образцом , где вы можете указать , названные категории , что ваши данные могут попасть в, а затем использовать эти категории в 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

Частичные активные шаблоны могут использоваться как форма тестирования, независимо от того, попадает ли вход в определенную категорию во входное пространство, игнорируя другие параметры.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow