Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Шаблоны — это правила преобразования входных данных. Они используются на языке F# для сравнения данных с логическими структурами или структурами, декомпилировать данные в составные части или извлекать информацию из данных различными способами.
Замечания
Шаблоны используются во многих языковых конструкциях, таких как выражение match. Они используются при обработке аргументов для функций в let привязках, лямбда-выражениях и в обработчиках исключений, связанных с выражением try...with. Дополнительные сведения см. в разделе сопоставления выражений, привязки let, лямбда-выражения: ключевое слово fun, и исключения: выражение try...with.
Например, в выражении match шаблон идет после символа канала.
match expression with
| pattern [ when condition ] -> result-expression
...
Каждый шаблон действует как правило для преобразования входных данных каким-то образом. В выражении match каждый шаблон в свою очередь проверяется, совместим ли входные данные с шаблоном. При обнаружении совпадения выполняется выражение результата. Если совпадение не найдено, проверяется следующее правило шаблона. Необязательная часть с условием разъясняется в конструкциях сопоставления .
Поддерживаемые шаблоны показаны в следующей таблице. Во время выполнения входные данные проверяются по каждому из следующих шаблонов в порядке, указанном в таблице, и шаблоны применяются рекурсивно, от первого до последнего, как они отображаются в коде, и слева направо для шаблонов в каждой строке.
| Имя | Описание | Пример |
|---|---|---|
| Постоянный шаблон | Любой числовый, символьный или строковый литерал, константы перечисления или определенный литеральный идентификатор |
1.0, , "test"30Color.Red |
| Шаблон идентификатора | Значение варианта дискриминированного объединения, метка исключения или случай активного шаблона | Some(x)Failure(msg) |
| Шаблон переменной | идентификатор | a |
шаблон as |
шаблон как идентификатор | (a, b) as tuple1 |
| Шаблон OR | шаблон1 | шаблон2 | ([h] | [h; _]) |
| И шаблон | pattern1 & pattern2 | (a, b) & (_, "test") |
| Шаблон Cons | идентификатор :: идентификатор списка | 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 |
| Шаблон Nameof | nameof expr | nameof str |
Шаблоны констант
Шаблоны констант — числовые, символьные и строковые литералы, константы перечисления (с включенным именем типа перечисления). Выражение match, которое имеет только константные шаблоны, можно сравнить с оператором case на других языках. Входные данные сравниваются с буквальным значением, и шаблон совпадает, если значения равны. Тип литерала должен быть совместим с типом входных данных.
В следующем примере показано использование литеральных шаблонов, а также использование шаблона переменной и шаблона OR.
[<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
Шаблоны идентификаторов
Если шаблон представляет собой строку символов, которая формирует допустимый идентификатор, форма идентификатора определяет соответствие шаблона. Если идентификатор длиннее одного символа и начинается с верхнего регистра, компилятор пытается выполнить сопоставление с шаблоном идентификатора. Идентификатор этого шаблона может быть значением, помеченным атрибутом Литерала, дискриминированным случаем объединения, идентификатором исключения или активным случаем шаблона. Если подходящий идентификатор не найден, процесс сопоставления завершается ошибкой, и следующее правило, шаблон переменной, сравнивается с входными данными.
Дискриминационные шаблоны объединения могут быть простыми именованными случаями или иметь значение, или кортеж, содержащий несколько значений. Если имеется значение, необходимо указать идентификатор значения. В случае кортежа необходимо указать шаблон кортежа с идентификатором для каждого элемента кортежа или идентификатора с именем поля для одного или нескольких именованных полей объединения. Примеры кода см. в этом разделе.
Тип 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 привязывает соответствующее значение к имени, которое можно использовать в выражении выполнения выражения match или, в случае, если этот шаблон используется в привязке let, имя добавляется в качестве привязки к локальной области.
В следующем примере используется шаблон as.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
ИЛИ шаблон
Шаблон OR используется, если входные данные могут соответствовать нескольким шаблонам, и вы хотите выполнить один и тот же код в результате. Типы обеих сторон шаблона OR должны быть совместимыми.
В следующем примере показан шаблон OR.
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)
И шаблон
Шаблон AND требует, чтобы входные данные соответствовали двум шаблонам. Типы обеих сторон шаблона AND должны быть совместимыми.
Следующий пример похож на detectZeroTuple, показанный в разделе шаблонов кортежей далее в этом разделе, но здесь оба var1 и var2 получаются в виде значений с помощью шаблона AND.
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)
Шаблон недостатков
Шаблон cons используется для разложения списка на первый элемент, голову, и список, содержащий остальные элементы, хвост.
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
Кроме того, можно объединить несколько шаблонов конусов для сопоставления списков, которые начинаются с определенных последовательностей элементов.
let charList = ['A'; 'B'; 'C'; 'D']
// This example demonstrates multiple cons patterns.
let matchChars xs =
match xs with
| 'A'::'B'::t -> printfn "starts with 'AB', rest: %A" t
| 'A'::t -> printfn "starts with 'A', rest: %A" t
| 'C'::'D'::t -> printfn "starts with 'CD', rest: %A" t
| _ -> printfn "does not match"
matchChars charList
matchChars ['A'; 'X']
matchChars ['C'; 'D'; 'E']
Шаблон списка
Шаблон списка позволяет разложить списки на ряд элементов. Сам шаблон списка может соответствовать только спискам определенного количества элементов.
// 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 [| |] )
Схема в круглых скобках
Круглые скобки можно сгруппировать по шаблонам, чтобы добиться требуемой ассоциативности. В следующем примере скобки используются для управления ассоциативностью между шаблоном AND и шаблоном минусов.
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"
Шаблон подстановочного знака
Шаблон подстановочного знака представлен символом подчеркивания (_) и соответствует любым входным данным, как и шаблон переменной, за исключением того, что входные данные удаляются вместо присвоения переменной. Шаблон подстановочного знака часто используется в других шаблонах в качестве заполнителя для значений, которые не нужны в выражении справа от символа ->. Шаблон подстановочных знаков также часто используется в конце списка шаблонов для сопоставления любых несовпаденных входных данных. Шаблон подстановочного знака демонстрируется во многих примерах кода в этом разделе. См. предыдущий пример кода.
В следующем коде показаны некоторые дополнительные способы использования шаблона подстановочных знаков:
// Wildcard pattern matching "nothing" examples
// Example 1: Wildcard ignoring function parameters
let ignoreAllParams _ _ = "Ignores all input"
// Example 2: Wildcard in destructuring, ignoring elements
let getFirstOnly (first, _) = first
// Example 3: Using wildcard to ignore optional values
let handleEmpty opt =
match opt with
| Some _ -> "Has something"
| None -> "Has nothing"
// Usage
printfn "%s" (ignoreAllParams 42 "test")
printfn "%d" (getFirstOnly (1, "ignored"))
printfn "%s" (handleEmpty None)
Паттерны с аннотациями типов
У шаблонов могут быть аннотации типов. Они ведут себя как другие аннотации типов и направляют вывод, как и другие аннотации типов. Круглые скобки являются обязательными для заметок типов в шаблонах.
Шаблон с заметкой типа использует синтаксис pattern : type и предоставляет сведения о типе компиляции в средство проверки типов. Это исключительно заметка статического типа, которая помогает с выводом типов. Она не выполняет проверку или преобразование типов среды выполнения. Компилятор использует эти сведения во время компиляции для определения типа переменной шаблона.
В следующем коде показан шаблон с заметкой типа:
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
В этом примере (var1 : int) сообщает компилятору тип var1int. Это разрешается во время компиляции, а созданный код обрабатывается var1 как целое число во всем выражении соответствия. Этот шаблон будет соответствовать любому целочисленного значения и привязать его к var1.
Ключевые характеристики:
- Использует синтаксис
pattern : type(с одной двоеточием). - Разрешено во время компиляции — предоставляет сведения о типе для средства проверки типов.
- Не выполняет тестирование типов среды выполнения.
- Используется для вывода типов и руководства компилятором.
Образец проверки типа
Шаблон теста типа используется для сопоставления входных данных с типом во время выполнения. Если входной тип соответствует типу (или производного типа), указанному в шаблоне, совпадение завершается успешно.
Шаблон теста типа использует синтаксис :? type и выполняет проверку типов среды выполнения, как isas и операторы в C#. Этот шаблон проверяет, является ли значение определенным типом во время выполнения программы, что делает его полезным при работе с иерархиями наследования или реализациями интерфейса.
В следующем примере демонстрируется шаблон теста типа:
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"
| _ -> ()
Ключевые характеристики:
- Использует синтаксис или
:? type as identifier(с вопросительным:? typeзнаком). - Разрешено во время выполнения — выполняет проверку фактического типа во время выполнения.
- Проверяет, является ли значение экземпляром определенного типа или производными типами.
- Часто используется с иерархиями наследования и полиморфными типами.
- Аналогично оператору или
asоператоруisC#.
Контрастные шаблоны примечаний типов и типов
Хотя оба шаблона включают типы, они служат очень разными целями:
| Функция | Шаблон заметки типа (pattern : type) |
Шаблон теста типа (:? type) |
|---|---|---|
| Syntax | Одна двоеточие: a : int |
Двоеточие с вопросительным знаком: :? Button |
| При разрешении | Время компиляции | Режим выполнения |
| Purpose | Вывод типов направляющих | Проверяет фактический тип значения |
| Вариант использования | Помогая компилятору понять типы | Проверка типов среды выполнения в иерархиях наследования |
| Эквивалент в C# | Ввод заметок в шаблонах коммутаторов |
is операторы или as операторы |
В следующем примере показаны различия.
// Type annotation pattern - compile time
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
// The ': int' tells the compiler var1 is an int
// Everything is resolved at compile time
// Type test pattern - runtime
type A() = class end
type B() = inherit A()
let test (a: A) =
match a with
| :? B -> printfn "Runtime check: it's a B"
| _ -> printfn "Runtime check: it's not a B"
// The ':? B' performs a runtime type check
// The actual type is tested during execution
Шаблон 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()
Шаблон NULL также рекомендуется использовать для возможностей F# 9 nullability:
let len (str: string | null) =
match str with
| null -> -1
| s -> s.Length
Аналогичным образом можно использовать новые специальные шаблоны, связанные с null ,:
let len str = // str is inferred to be `string | null`
match str with
| Null -> -1
| NonNull (s: string) -> s.Length
Шаблон Nameof
Шаблон nameof соответствует строке, если его значение равно выражению, которое следует ключевому слову nameof. Этот шаблон особенно полезен, если необходимо сопоставить строковые значения с именами типов, дискриминированными случаями объединения или другими символами в коде. Использование nameof обеспечивает безопасность во время компиляции, так как при переименовании символа шаблон автоматически будет использовать новое имя.
Распространенный вариант использования — десериализация данных, в которых строковые значения представляют имена типов или вариантов:
type EventType =
| OrderCreated
| OrderShipped
| OrderDelivered
let handleEvent eventName data =
match eventName with
| nameof OrderCreated -> printfn "Processing order creation: %s" data
| nameof OrderShipped -> printfn "Processing order shipment: %s" data
| nameof OrderDelivered -> printfn "Processing order delivery: %s" data
| _ -> printfn "Unknown event type: %s" eventName
handleEvent "OrderCreated" "Order #123" // matches first case
handleEvent "OrderShipped" "Order #123" // matches second case
Этот подход лучше, чем использовать строковые литералы (например), так как "OrderCreated":
- При переименовании
OrderCreatedOrderPlacedв шаблон автоматически обновляется. - Компилятор гарантирует, что символ существует, предотвращая опечатки.
- Код остается согласованным при рефакторинге.
Можно также использовать 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 для получения сведений о том, чему можно присвоить имя.
См. также
- выражения сопоставления
- Активные шаблоны
- Справочник по языку F#