Сопоставление шаблонов

Шаблоны — это правила для преобразования входных данных. Они используются в F # для сравнения данных с логической структурой или структурами, разложения данных в составные части или извлечения информации из данных различными способами.

Комментарии

Шаблоны используются во многих языковых конструкциях, таких как match выражение. Они используются при обработке аргументов для функций в let привязках, лямбда-выражениях и в обработчиках исключений, связанных с try...with выражением. Дополнительные сведения см. в разделе выражения Match, Привязка let, лямбда-выражения: fun ключевое словои исключения: try...with выражение.

Например, в match выражении шаблон соответствует символу вертикальной черты.

match expression with
| pattern [ when condition ] -> result-expression
...

Каждый шаблон выступает в качестве правила для преобразования входных данных каким-либо образом. В match выражении каждый шаблон рассматривается в, в свою очередь, для того, чтобы проверить, совместимы ли входные данные с шаблоном. При обнаружении совпадения выполняется результирующее выражение. Если совпадение не найдено, проверяется следующее правило шаблона. Необязательная часть условия when объясняется в выражениях Match.

Поддерживаемые шаблоны показаны в следующей таблице. Во время выполнения входные данные тестируются по каждому из следующих шаблонов в порядке, указанном в таблице, а шаблоны применяются рекурсивно, от первого до последнего, как они отображаются в коде, и слева направо для шаблонов в каждой строке.

Имя Описание Пример
Шаблон константы Любой числовой, символьный или строковый литерал, константа перечисления или определенный литеральный идентификатор 1.0, "test", 30, Color.Red
Шаблон идентификатора Значение варианта для размеченного объединения, метки исключения или активного варианта шаблона Some(x)

Failure(msg)
Шаблон переменной identifier a
Шаблон as шаблон в качестве идентификатора (a, b) as tuple1
ИЛИ шаблон pattern1 | pattern2 ([h] | [h; _])
И шаблон pattern1 & pattern2 (a, b) & (_, "test")
Шаблон «против» идентификатор :: List-identifier h :: t
Шаблон списка [ pattern_1;...; pattern_n ] [ a; b; c ]
Шаблон массива [| pattern_1;..; pattern_n |] [| a; b; c |]
Шаблон в круглых скобках ( шаблон ) ( a )
Шаблон кортежа ( pattern_1,..., pattern_n ) ( a, b )
Шаблон записи { идентификатор1 = pattern_1; ... ; identifier_n = pattern_n } { Name = name; }
Шаблон подстановочного знака _ _
Шаблон вместе с аннотацией типа шаблон : тип a : int
Шаблон проверки типа :? введите [как идентификатор ] :? System.DateTime as dt
Шаблон NULL null null
Шаблон NameOf NameOf expr nameof str

Шаблоны констант

Шаблоны констант — это числовые, символьные и строковые литералы, константы перечисления (с включаемым именем типа перечисления). matchВыражение, имеющее только постоянные шаблоны, можно сравнивать с оператором case на других языках. Входные данные сравниваются с литеральным значением, а шаблон соответствует, если значения равны. Тип литерала должен быть совместим с типом входных данных.

В следующем примере демонстрируется использование литеральных шаблонов, а также используется шаблон переменной и шаблон или.

[<Literal>]
let Three = 3

let filter123 x =
    match x with
    // The following line contains literal patterns combined with an OR pattern.
    | 1 | 2 | Three -> printfn "Found 1, 2, or 3!"
    // The following line contains a variable pattern.
    | var1 -> printfn "%d" var1

for x in 1..10 do filter123 x

Еще один пример литерального шаблона — это шаблон, основанный на константах перечисления. При использовании констант перечисления необходимо указать имя типа перечисления.

type Color =
    | Red = 0
    | Green = 1
    | Blue = 2

let printColorName (color:Color) =
    match color with
    | Color.Red -> printfn "Red"
    | Color.Green -> printfn "Green"
    | Color.Blue -> printfn "Blue"
    | _ -> ()

printColorName Color.Red
printColorName Color.Green
printColorName Color.Blue

Шаблоны идентификаторов

Если шаблон представляет собой строку символов, образующих допустимый идентификатор, форма идентификатора определяет способ сопоставления шаблона. Если идентификатор длиннее одного символа и начинается с символа верхнего регистра, компилятор пытается выполнить сопоставление с шаблоном идентификатора. Идентификатором для этого шаблона может быть значение, помеченное атрибутом Literal, вариантом размеченного объединения, идентификатором исключения или активным шаблоном. Если соответствующий идентификатор не найден, сопоставление завершается ошибкой, а следующее правило шаблона, шаблон переменной, сравнивается с входными данными.

Шаблоны размеченного объединения могут быть простыми именованными вариантами или иметь значение или кортеж, содержащий несколько значений. Если имеется значение, необходимо указать идентификатор для этого значения. В случае кортежа необходимо предоставить шаблон кортежа с идентификатором для каждого элемента кортежа или идентификатором с именем поля для одного или нескольких именованных полей объединения. Примеры см. в примерах кода в этом разделе.

optionТип — это размеченное объединение, которое имеет два варианта: Some и None . В одном варианте ( Some ) есть значение, а другое ( None ) — это просто именованное обращение. Следовательно, необходимо Some иметь переменную для значения, связанного с Some вариантом, но None должно быть само по себе. В следующем коде переменной var1 присваивается значение, полученное путем сопоставления с Some вариантом.

let printOption (data : int option) =
    match data with
    | Some var1  -> printfn "%d" var1
    | None -> ()

В следующем примере PersonName размеченное объединение содержит сочетание строк и символов, представляющих возможные формы имен. Размеченные объединения: FirstOnly , LastOnly и FirstLast .

type PersonName =
    | FirstOnly of string
    | LastOnly of string
    | FirstLast of string * string

let constructQuery personName =
    match personName with
    | FirstOnly(firstName) -> printf "May I call you %s?" firstName
    | LastOnly(lastName) -> printf "Are you Mr. or Ms. %s?" lastName
    | FirstLast(firstName, lastName) -> printf "Are you %s %s?" firstName lastName

Для размеченных объединений с именованными полями используется знак равенства (=) для извлечения значения именованного поля. Например, рассмотрим размеченное объединение с объявлением, как показано ниже.

type Shape =
    | Rectangle of height : float * width : float
    | Circle of radius : float

Именованные поля можно использовать в выражении сопоставления шаблонов следующим образом.

let matchShape shape =
    match shape with
    | Rectangle(height = h) -> printfn $"Rectangle with length %f{h}"
    | Circle(r) -> printfn $"Circle with radius %f{r}"

Использование именованного поля является необязательным, поэтому в предыдущем примере оба Circle(r) и Circle(radius = r) имеют одинаковый результат.

При указании нескольких полей используйте точку с запятой (;) в качестве разделителя.

match shape with
| Rectangle(height = h; width = w) -> printfn $"Rectangle with height %f{h} and width %f{w}"
| _ -> ()

Активные шаблоны позволяют определить более сложное сопоставление пользовательских шаблонов. Дополнительные сведения об активных шаблонах см. в разделе Активные закономерности.

Случай, когда идентификатор является исключением, используется в сопоставлении шаблонов в контексте обработчиков исключений. Сведения о сопоставлении шаблонов в обработке исключений см. в разделе исключения: try...with выражение.

Шаблоны переменных

Шаблон переменной присваивает значение, совпадающее с именем переменной, которое затем можно использовать в выражении выполнения справа от -> символа. Только шаблон переменной соответствует любым входным данным, но шаблоны переменных часто появляются в других шаблонах, поэтому для реализации более сложных структур, таких как кортежи и массивы, следует разложить их на переменные.

В следующем примере демонстрируется шаблон переменной в шаблоне кортежа.

let function1 x =
    match x with
    | (var1, var2) when var1 > var2 -> printfn "%d is greater than %d" var1 var2
    | (var1, var2) when var1 < var2 -> printfn "%d is less than %d" var1 var2
    | (var1, var2) -> printfn "%d equals %d" var1 var2

function1 (1,2)
function1 (2, 1)
function1 (0, 0)

Шаблон as

asШаблон — это шаблон, as к которому добавляется предложение. asПредложение привязывает совпадающее значение к имени, которое может использоваться в выражении выполнения match выражения, или, если этот шаблон используется в let привязке, имя добавляется в качестве привязки в локальную область.

В следующем примере используется as шаблон.

let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1

ИЛИ шаблон

Шаблон или используется, если входные данные могут соответствовать нескольким шаблонам и вы хотите выполнить тот же код в результате. Типы обеих сторон шаблона или должны быть совместимы.

В следующем примере демонстрируется шаблон или.

let detectZeroOR point =
    match point with
    | (0, 0) | (0, _) | (_, 0) -> printfn "Zero found."
    | _ -> printfn "Both nonzero."
detectZeroOR (0, 0)
detectZeroOR (1, 0)
detectZeroOR (0, 10)
detectZeroOR (10, 15)

И шаблон

Шаблон и требует, чтобы входные данные соответствовали двум шаблонам. Типы обеих сторон шаблона и должны быть совместимы.

Следующий пример аналогичен detectZeroTuple показанному в разделе Шаблон кортежа далее в этом разделе, но здесь var1 и и var2 получаются в виде значений с помощью шаблона и.

let detectZeroAND point =
    match point with
    | (0, 0) -> printfn "Both values zero."
    | (var1, var2) & (0, _) -> printfn "First value is 0 in (%d, %d)" var1 var2
    | (var1, var2)  & (_, 0) -> printfn "Second value is 0 in (%d, %d)" var1 var2
    | _ -> printfn "Both nonzero."
detectZeroAND (0, 0)
detectZeroAND (1, 0)
detectZeroAND (0, 10)
detectZeroAND (10, 15)

Шаблон «против»

Шаблон недостатков используется для разбиения списка на первый элемент, заголовок и список, содержащий остальные элементы, хвост.

let list1 = [ 1; 2; 3; 4 ]

// This example uses a cons pattern and a list pattern.
let rec printList l =
    match l with
    | head :: tail -> printf "%d " head; printList tail
    | [] -> printfn ""

printList list1

Шаблон списка

Шаблон списка позволяет разложить списки на несколько элементов. Сам шаблон списка может сопоставлять только списки определенного числа элементов.

// This example uses a list pattern.
let listLength list =
    match list with
    | [] -> 0
    | [ _ ] -> 1
    | [ _; _ ] -> 2
    | [ _; _; _ ] -> 3
    | _ -> List.length list

printfn "%d" (listLength [ 1 ])
printfn "%d" (listLength [ 1; 1 ])
printfn "%d" (listLength [ 1; 1; 1; ])
printfn "%d" (listLength [ ] )

Шаблон массива

Шаблон массива напоминает шаблон списка и может использоваться для разложения массивов определенной длины.

// This example uses array patterns.
let vectorLength vec =
    match vec with
    | [| var1 |] -> var1
    | [| var1; var2 |] -> sqrt (var1*var1 + var2*var2)
    | [| var1; var2; var3 |] -> sqrt (var1*var1 + var2*var2 + var3*var3)
    | _ -> failwith (sprintf "vectorLength called with an unsupported array size of %d." (vec.Length))

printfn "%f" (vectorLength [| 1. |])
printfn "%f" (vectorLength [| 1.; 1. |])
printfn "%f" (vectorLength [| 1.; 1.; 1.; |])
printfn "%f" (vectorLength [| |] )

Шаблон в круглых скобках

Круглые скобки могут быть сгруппированы вокруг шаблонов для достижения требуемой ассоциативности. В следующем примере круглые скобки используются для управления ассоциативностью между шаблоном и и шаблоном недостатка.

let countValues list value =
    let rec checkList list acc =
       match list with
       | (elem1 & head) :: tail when elem1 = value -> checkList tail (acc + 1)
       | head :: tail -> checkList tail acc
       | [] -> acc
    checkList list 0

let result = countValues [ for x in -10..10 -> x*x - 4 ] 0
printfn "%d" result

Шаблон кортежа

Шаблон кортежа соответствует входным данным в форме кортежа и позволяет разложить кортеж в составные элементы с помощью переменных сопоставления шаблонов для каждой из позиций в кортеже.

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

let detectZeroTuple point =
    match point with
    | (0, 0) -> printfn "Both values zero."
    | (0, var2) -> printfn "First value is 0 in (0, %d)" var2
    | (var1, 0) -> printfn "Second value is 0 in (%d, 0)" var1
    | _ -> printfn "Both nonzero."
detectZeroTuple (0, 0)
detectZeroTuple (1, 0)
detectZeroTuple (0, 10)
detectZeroTuple (10, 15)

Шаблон записи

Шаблон записи используется для разложения записей, чтобы извлечь значения полей. Шаблон не должен ссылаться на все поля записи; все пропущенные поля просто не участвуют в сопоставлении и не извлекаются.

// This example uses a record pattern.

type MyRecord = { Name: string; ID: int }

let IsMatchByName record1 (name: string) =
    match record1 with
    | { MyRecord.Name = nameFound; MyRecord.ID = _; } when nameFound = name -> true
    | _ -> false

let recordX = { Name = "Parker"; ID = 10 }
let isMatched1 = IsMatchByName recordX "Parker"
let isMatched2 = IsMatchByName recordX "Hartono"

Шаблон подстановочного знака

Шаблон подстановочного знака представлен символом подчеркивания ( _ ) и соответствует любым входным данным, как и шаблону переменной, за исключением того, что входные данные удаляются, а не присваиваются переменной. Шаблон шаблона часто используется в других шаблонах в качестве заполнителя для значений, которые не требуются в выражении справа от -> символа. Шаблон с подстановочным знаком также часто используется в конце списка шаблонов для сопоставления с любыми несоответствующими входными данными. Шаблон с подстановочными знаками демонстрируется во многих примерах кода в этом разделе. См. Приведенный выше код для одного примера.

Шаблоны с аннотациями типов

Шаблоны могут иметь аннотации типов. Они ведут себя так же, как и другие аннотации типа и пошаговое определение, как и другие заметки типа. Для заметок типа в шаблонах требуются круглые скобки. В следующем коде показан шаблон с аннотацией типа.

let detect1 x =
    match x with
    | 1 -> printfn "Found a 1!"
    | (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1

Шаблон проверки типа

Шаблон проверки типа используется для сопоставления входных данных с типом. Если тип входных данных соответствует типу (или производному типу) типа, указанного в шаблоне, сопоставление выполняется с ошибкой.

В следующем примере показан шаблон проверки типа.

open System.Windows.Forms

let RegisterControl(control:Control) =
    match control with
    | :? Button as button -> button.Text <- "Registered."
    | :? CheckBox as checkbox -> checkbox.Text <- "Registered."
    | _ -> ()

Если проверяется только идентификатор определенного производного типа, то as identifier часть шаблона не требуется, как показано в следующем примере:

type A() = class end
type B() = inherit A()
type C() = inherit A()

let m (a: A) =
    match a with
    | :? B -> printfn "It's a B"
    | :? C -> printfn "It's a C"
    | _ -> ()

Шаблон NULL

Шаблон NULL соответствует значению NULL, которое может появиться при работе с типами, допускающими значение null. шаблоны Null часто используются при взаимодействии с платформа .NET Frameworkным кодом. Например, возвращаемое значение API-интерфейса .NET может быть входным для match выражения. Можно управлять выполнением программы в зависимости от того, имеет ли возвращаемое значение значение null, а также другие характеристики возвращаемого значения. Можно использовать шаблон NULL, чтобы предотвратить распространение значений NULL на остальную часть программы.

В следующем примере используется шаблон NULL и шаблон переменной.

let ReadFromFile (reader : System.IO.StreamReader) =
    match reader.ReadLine() with
    | null -> printfn "\n"; false
    | line -> printfn "%s" line; true

let fs = System.IO.File.Open("..\..\Program.fs", System.IO.FileMode.Open)
let sr = new System.IO.StreamReader(fs)
while ReadFromFile(sr) = true do ()
sr.Close()

Шаблон NameOf

nameofШаблон соответствует строке, если его значение равно выражению, которое следует за nameof ключевым словом. Например:

let f (str: string) =
    match str with
    | nameof str -> "It's 'str'!"
    | _ -> "It is not 'str'!"

f "str" // matches
f "asdf" // does not match

Сведения о nameof том, что можно сделать, см. в операторе.

См. также