Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
активные шаблоны позволяют определять именованные секции, которые подразделяют входные данные, чтобы использовать эти имена в выражении сопоставления шаблонов так же, как и для дискриминированного объединения. Активные шаблоны можно использовать для разбиения данных в настраиваемом режиме для каждой секции.
Синтаксис
// Active pattern of one choice.
let (|identifier|) [arguments] valueToMatch = expression
// Active Pattern with multiple choices.
// Uses a FSharp.Core.Choice<_,...,_> based on the number of case names. In F#, the limitation n <= 7 applies.
let (|identifier1|identifier2|...|) valueToMatch = expression
// Partial active pattern definition.
// Can use FSharp.Core.option<_>, FSharp.Core.voption<_> or bool to represent if the type is satisfied at the call site.
let (|identifier|_|) [arguments] valueToMatch = expression
Замечания
В предыдущем синтаксисе идентификаторы — это имена для секций входных данных, представленных аргументамиили, другими словами, имена для подмножества набора всех значений аргументов. В активном определении шаблона может быть до семи секций. Выражение описывает форму, в которую необходимо разложить данные. Можно использовать активное определение шаблона для определения правил определения того, к каким из именованных секций относятся значения, заданные в качестве аргументов. Символы (| и |) называются банановыми клипсами, а функция, созданная этим типом привязки, называется активным распознавателем.
В качестве примера рассмотрим следующий активный шаблон с аргументом.
let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd
Активный шаблон можно использовать в выражении сопоставления шаблонов, как показано в следующем примере.
let TestNumber input =
match input with
| Even -> printfn "%d is even" input
| Odd -> printfn "%d is odd" input
TestNumber 7
TestNumber 11
TestNumber 32
Выходные данные этой программы приведены следующим образом:
7 is odd
11 is odd
32 is even
Другое использование активных шаблонов заключается в разложении типов данных несколькими способами, например, когда одни и те же базовые данные имеют различные возможные представления. Например, объект Color может быть разложен в представление RGB или представление HSB.
open System.Drawing
let (|RGB|) (col : System.Drawing.Color) =
( col.R, col.G, col.B )
let (|HSB|) (col : System.Drawing.Color) =
( col.GetHue(), col.GetSaturation(), col.GetBrightness() )
let printRGB (col: System.Drawing.Color) =
match col with
| RGB(r, g, b) -> printfn " Red: %d Green: %d Blue: %d" r g b
let printHSB (col: System.Drawing.Color) =
match col with
| HSB(h, s, b) -> printfn " Hue: %f Saturation: %f Brightness: %f" h s b
let printAll col colorString =
printfn "%s" colorString
printRGB col
printHSB col
printAll Color.Red "Red"
printAll Color.Black "Black"
printAll Color.White "White"
printAll Color.Gray "Gray"
printAll Color.BlanchedAlmond "BlanchedAlmond"
Выходные данные приведенной выше программы приведены следующим образом:
Red
Red: 255 Green: 0 Blue: 0
Hue: 360.000000 Saturation: 1.000000 Brightness: 0.500000
Black
Red: 0 Green: 0 Blue: 0
Hue: 0.000000 Saturation: 0.000000 Brightness: 0.000000
White
Red: 255 Green: 255 Blue: 255
Hue: 0.000000 Saturation: 0.000000 Brightness: 1.000000
Gray
Red: 128 Green: 128 Blue: 128
Hue: 0.000000 Saturation: 0.000000 Brightness: 0.501961
BlanchedAlmond
Red: 255 Green: 235 Blue: 205
Hue: 36.000000 Saturation: 1.000000 Brightness: 0.901961
В сочетании эти два способа использования активных шаблонов позволяют секционировать и разлагать данные в соответствующую форму и выполнять соответствующие вычисления по соответствующим данным в форме, наиболее удобной для вычисления.
Результирующее выражение сопоставления шаблонов позволяет записывать данные удобным способом, который очень удобочитаемый, что значительно упрощает потенциально сложный код ветвления и анализа данных.
Частичные активные шаблоны
Иногда необходимо секционировать только часть входного пространства. В этом случае создается набор частичных шаблонов, каждый из которых соответствует некоторым входным данным, но не соответствует другим входным данным. Активные шаблоны, которые не всегда создают значение, называются частичными активными шаблонами; Они имеют возвращаемое значение, которое является типом параметра. Чтобы определить частичный активный шаблон, вы используете подстановочный знак (_) в конце списка шаблонов внутри банановых клипов. Следующий код иллюстрирует использование частично активного шаблона.
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let (|Float|_|) (str: string) =
let mutable floatvalue = 0.0
if System.Double.TryParse(str, &floatvalue) then Some(floatvalue)
else None
let parseNumeric str =
match str with
| Integer i -> printfn "%d : Integer" i
| Float f -> printfn "%f : Floating point" f
| _ -> printfn "%s : Not matched." str
parseNumeric "1.1"
parseNumeric "0"
parseNumeric "0.0"
parseNumeric "10"
parseNumeric "Something else"
Выходные данные предыдущего примера приведены следующим образом:
1.100000 : Floating point
0 : Integer
0.000000 : Floating point
10 : Integer
Something else : Not matched.
При использовании частичных активных шаблонов иногда отдельные варианты могут быть несвязанными или взаимоисключающими, но они не должны быть. В следующем примере шаблоны Квадрата и Куба неразделимы, так как некоторые числа являются и квадратами, и кубами, например 64. Следующая программа использует шаблон AND для объединения шаблонов квадратов и кубов. Он выводит все целые числа до 1000, которые являются как квадратами, так и кубами, а также теми, которые являются только кубами.
let err = 1.e-10
let isNearlyIntegral (x:float) = abs (x - round(x)) < err
let (|Square|_|) (x : int) =
if isNearlyIntegral (sqrt (float x)) then Some(x)
else None
let (|Cube|_|) (x : int) =
if isNearlyIntegral ((float x) ** ( 1.0 / 3.0)) then Some(x)
else None
let findSquareCubes x =
match x with
| Cube x & Square _ -> printfn "%d is a cube and a square" x
| Cube x -> printfn "%d is a cube" x
| _ -> ()
[ 1 .. 1000 ] |> List.iter (fun elem -> findSquareCubes elem)
Выходные данные приведены следующим образом:
1 is a cube and a square
8 is a cube
27 is a cube
64 is a cube and a square
125 is a cube
216 is a cube
343 is a cube
512 is a cube
729 is a cube and a square
1000 is a cube
Параметризованные активные шаблоны
Активные шаблоны всегда принимают как минимум один аргумент для элемента, который нужно сопоставить, но могут также принимать дополнительные аргументы; в таком случае применяется название параметризованного активного шаблона . Дополнительные аргументы позволяют уточнить общий шаблон. Например, активные шаблоны, использующие регулярные выражения для анализа строк, часто включают регулярное выражение в качестве дополнительного параметра, как в следующем коде, который также использует частичный активный шаблон Integer, определенный в предыдущем примере кода. В этом примере строки, использующие регулярные выражения для различных форматов дат, предоставляются для настройки общего активного шаблона ParseRegex. Активный шаблон integer используется для преобразования сопоставленных строк в целые числа, которые можно передать в конструктор DateTime.
open System.Text.RegularExpressions
// ParseRegex parses a regular expression and returns a list of the strings that match each group in
// the regular expression.
// List.tail is called to eliminate the first element in the list, which is the full matched expression,
// since only the matches for each group are wanted.
let (|ParseRegex|_|) regex str =
let m = Regex(regex).Match(str)
if m.Success
then Some (List.tail [ for x in m.Groups -> x.Value ])
else None
// Three different date formats are demonstrated here. The first matches two-
// digit dates and the second matches full dates. This code assumes that if a two-digit
// date is provided, it is an abbreviation, not a year in the first century.
let parseDate str =
match str with
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{1,2})$" [Integer m; Integer d; Integer y]
-> new System.DateTime(y + 2000, m, d)
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{3,4})" [Integer m; Integer d; Integer y]
-> new System.DateTime(y, m, d)
| ParseRegex "(\d{1,4})-(\d{1,2})-(\d{1,2})" [Integer y; Integer m; Integer d]
-> new System.DateTime(y, m, d)
| _ -> new System.DateTime()
let dt1 = parseDate "12/22/08"
let dt2 = parseDate "1/1/2009"
let dt3 = parseDate "2008-1-15"
let dt4 = parseDate "1995-12-28"
printfn "%s %s %s %s" (dt1.ToString()) (dt2.ToString()) (dt3.ToString()) (dt4.ToString())
Выходные данные предыдущего кода приведены следующим образом:
12/22/2008 12:00:00 AM 1/1/2009 12:00:00 AM 1/15/2008 12:00:00 AM 12/28/1995 12:00:00 AM
Активные шаблоны не ограничиваются только выражениями сопоставления шаблонов, вы также можете использовать их для привязок let-bindings.
let (|Default|) onNone value =
match value with
| None -> onNone
| Some e -> e
let greet (Default "random citizen" name) =
printfn "Hello, %s!" name
greet None
greet (Some "George")
Выходные данные предыдущего кода приведены следующим образом:
Hello, random citizen!
Hello, George!
Обратите внимание, однако, что параметризовать можно только одиночные активные шаблоны.
// A single-case partial active pattern can be parameterized
let (| Foo|_|) s x = if x = s then Some Foo else None
// A multi-case active patterns cannot be parameterized
// let (| Even|Odd|Special |) (s: int) (x: int) = if x = s then Special elif x % 2 = 0 then Even else Odd
Тип возврата для частичных активных шаблонов
Частичные активные шаблоны выдают Some () для указания совпадения и None в противном случае.
Рассмотрим это совпадение:
match key with
| CaseInsensitive "foo" -> ...
| CaseInsensitive "bar" -> ...
Частичный активный шаблон для него будет следующим:
let (|CaseInsensitive|_|) (pattern: string) (value: string) =
if String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase) then
Some ()
else
None
Начиная с F# 9, такие шаблоны также могут возвращать bool:
let (|CaseInsensitive|_|) (pattern: string) (value: string) =
String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase)
Представления структуры для частичных активных шаблонов
По умолчанию, если частичный активный шаблон возвращает option, это приведет к выделению памяти для значения Some при успешном совпадении. Чтобы избежать этого, можно использовать значение опции в качестве возвращаемого значения через использование атрибута Struct:
open System
[<return: Struct>]
let (|Int|_|) str =
match Int32.TryParse(str) with
| (true, n) -> ValueSome n
| _ -> ValueNone
Атрибут должен быть указан, так как использование структуры возвращаемого значения не выводится из простого изменения типа возврата на ValueOption. Дополнительные сведения см. в RFC FS-1039.
Нулевые активные шаблоны
В F# 9 добавлены активные шаблоны, связанные со значением NULL.
Первым является | Null | NonNull x |, который рекомендуется для обработки возможных null. В следующем примере параметр s считается допускающим значение NULL посредством использования этой активной структуры:
let len s =
match s with
| Null -> -1
| NonNull s -> String.length s
Если вы предпочитаете автоматически инициировать NullReferenceException, можно использовать шаблон | NonNullQuick |.
let len (NonNullQuick str) = // throws if the argument is null
String.length str