9.6 Шаблоны
|
См. также следующие расширения языка: модули первого класса, атрибуты и узлы расширения.
В таблице ниже показаны относительные приоритеты и ассоциативность операторов и конструкций шаблонов, не имеющих закрытия. Конструкции с более высоким приоритетом идут первыми.
| Оператор | Ассоциативность |
| .. | – |
| lazy (см. раздел 9.6) | – |
| Применение конструктора, применение тега | справа налево |
| :: | справа налево |
| , | – |
| | | слева направо |
| as | – |
Шаблоны — это шаблоны, которые позволяют выбирать структуры данных заданной формы и связывать идентификаторы с компонентами структуры данных. Эта операция выбора называется сопоставлением с шаблоном; ее результат — либо «это значение не соответствует этому шаблону», либо «это значение соответствует этому шаблону, что приводит к следующим привязанностям имен к значениям».
Шаблоны переменных
Шаблон, состоящий из имени значения, соответствует любому значению, связывая имя со значением. Шаблон _ также соответствует любому значению, но не связывает никакого имени.
# let is_empty = function
| [] -> true
| _ :: _ -> false;;
val is_empty : 'a list -> bool = Шаблоны являются линейными: переменная не может быть связана несколько раз одним и тем же шаблоном. В частности, нет способа проверить равенство двух частей структуры данных, используя только шаблон:
# let pair_equal = function
| x, x -> true
| x, y -> false;;
Error: Variable x is bound several times in this matching Однако мы можем использовать условие when для этой цели:
# let pair_equal = function
| x, y when x = y -> true
| _ -> false;;
val pair_equal : 'a * 'a -> bool = Шаблоны констант
Шаблон, состоящий из константы, соответствует значениям, равным этой константе.
# let bool_of_string = function
| "true" -> true
| "false" -> false
| _ -> raise (Invalid_argument "bool_of_string");;
val bool_of_string : string -> bool = Шаблоны псевдонимов
Шаблон pattern1 as value-name соответствует тем же значениям, что и pattern1. Если сопоставление с pattern1 выполняется успешно, имя value-name связывается со значением, соответствующим шаблону, в дополнение к связям, выполненным при сопоставлении с pattern1.
# let sort_pair ((x, y) as p) =
if x <= y then p else (y, x);;
val sort_pair : 'a * 'a -> 'a * 'a = Шаблоны в скобках
Шаблон ( pattern1 ) сопоставляет те же значения, что и pattern1. Ограничение типа может появиться в скобочном шаблоне, как в ( pattern1 : typexpr ). Это ограничение требует, чтобы тип pattern1 был совместим с typexpr.
# let int_triple_is_ordered ((a, b, c) : int * int * int) =
a <= b && b <= c;;
val int_triple_is_ordered : int * int * int -> bool = Шаблоны «или»
Шаблон pattern1 | pattern2 представляет собой логическое «или» двух шаблонов pattern1 и pattern2. Значение соответствует pattern1 | pattern2, если оно соответствует pattern1 или pattern2. Два подшаблона pattern1 и pattern2 должны связывать идентификаторы точно к значениям с одинаковыми типами. Сопоставление выполняется слева направо. Более точно, в случае, когда некоторое значение v соответствует pattern1 | pattern2, выполняются привязки pattern1, когда v соответствует pattern1. В противном случае, значение v соответствует pattern2, привязки которого выполняются.
# type shape = Square of float | Rect of (float * float) | Circle of float
let is_rectangular = function
| Square _ | Rect _ -> true
| Circle _ -> false;;
type shape = Square of float | Rect of (float * float) | Circle of float
val is_rectangular : shape -> bool = Шаблоны вариантов
Шаблон constr ( pattern1 , … , patternn ) сопоставляет все варианты, конструктор которых равен constr, а аргументы соответствуют pattern1 … patternn. Ошибка типа возникает, если n не равно количеству аргументов, ожидаемых конструктором.
Шаблон constr _ соответствует всем вариантам, конструктор которых равен constr.
# type 'a tree = Lf | Br of 'a tree * 'a * 'a tree
let rec total = function
| Br (l, x, r) -> total l + x + total r
| Lf -> 0;;
type 'a tree = Lf | Br of 'a tree * 'a * 'a tree
val total : int tree -> int = Шаблон pattern1 :: pattern2 соответствует непустым спискам, у которых заголовки соответствуют pattern1, а хвосты соответствуют pattern2.
Шаблон [ pattern1 ; … ; patternn ] соответствует спискам длины n, элементы которых соответствуют pattern1 …patternn соответственно. Этот шаблон ведет себя как pattern1 :: … :: patternn :: [].
# let rec destutter = function
| [] -> []
| [a] -> [a]
| a :: b :: t -> if a = b then destutter (b :: t) else a :: destutter (b :: t);;
val destutter : 'a list -> 'a list = Шаблоны полиморфных вариантов
Шаблон `tag-name pattern1 сопоставляет все полиморфные варианты, тег которых равен tag-name, а аргумент соответствует pattern1.
# let rec split = function
| [] -> ([], [])
| h :: t ->
let ss, gs = split t in
match h with
| `Sheep _ as s -> (s :: ss, gs)
| `Goat _ as g -> (ss, g :: gs);;
val split :
[< `Goat of 'a | `Sheep of 'b ] list ->
[> `Sheep of 'b ] list * [> `Goat of 'a ] list = Шаблоны сокращений полиморфных вариантов
Если тип [('a,'b,…)] typeconstr = [ `tag-name1 typexpr1 | … | `tag-namen typexprn] определен, то шаблон #typeconstr является сокращением для следующего шаблона «или»: ( `tag-name1(_ : typexpr1) | … | `tag-namen(_ : typexprn)). Он сопоставляет все значения типа [< typeconstr ].
# type 'a rectangle = [`Square of 'a | `Rectangle of 'a * 'a]
type 'a shape = [`Circle of 'a | 'a rectangle]
let try_rectangle = function
| #rectangle as r -> Some r
| `Circle _ -> None;;
type 'a rectangle = [ `Rectangle of 'a * 'a | `Square of 'a ]
type 'a shape = [ `Circle of 'a | `Rectangle of 'a * 'a | `Square of 'a ]
val try_rectangle :
[< `Circle of 'a | `Rectangle of 'b * 'b | `Square of 'b ] ->
[> `Rectangle of 'b * 'b | `Square of 'b ] option = Шаблоны кортежей
Шаблон pattern1 , … , patternn сопоставляет n-кортежи, компоненты которых соответствуют шаблонам pattern1 до patternn. То есть, шаблон сопоставляет значения кортежа (v1, …, vn), такие что patterni сопоставляет vi для i = 1,… , n.
# let vector (x0, y0) (x1, y1) =
(x1 -. x0, y1 -. y0);;
val vector : float * float -> float * float -> float * float = Шаблоны записей
Шаблон { поле1 [= шаблон1] ; … ; полеn [= шаблонn] } сопоставляет записи, которые определяют по крайней мере поля поле1 до полеn, и такие, что значение, связанное с полемi, соответствует шаблону шаблонуi, для i = 1,… , n. Единственный идентификатор полеk обозначает полеk = полеk, а единственный квалифицированный идентификатор путь-модуля . полеk обозначает путь-модуля . полеk = полеk. Значение записи может определить больше полей, чем поле1 …полеn; значения, связанные с этими дополнительными полями, не учитываются при сопоставлении. Необязательно, шаблон записи может завершаться ; _ для того, чтобы указать на то, что не все поля типа записи перечислены в шаблоне записи и что это сделано намеренно. Необязательные ограничения типов могут быть добавлены поле за полем с { поле1 : типвыражение1 = шаблон1 ;… ;полеn : типвыражениеn = шаблонn } для принудительного приведения типа полеk к совместимому с типвыражениеk.
# let bytes_allocated
{Gc.minor_words = minor;
Gc.major_words = major;
Gc.promoted_words = prom;
_}
=
(Sys.word_size / 4) * int_of_float (minor +. major -. prom);;
val bytes_allocated : Gc.stat -> int = Шаблоны массивов
Шаблон [| шаблон1 ; … ; шаблонn |] соответствует массивам длины n таким, что i-й элемент массива соответствует шаблону шаблонi, для i = 1,… , n.
# let matrix3_is_symmetric = function
| [|[|_; b; c|];
[|d; _; f|];
[|g; h; _|]|] -> b = d && c = g && f = h
| _ -> failwith "matrix3_is_symmetric: not a 3x3 matrix";;
val matrix3_is_symmetric : 'a array array -> bool = Шаблоны диапазонов
Шаблон ' c ' .. ' d ' является сокращением для шаблона
где c1, c2, …, cn — это символы, которые встречаются между c и d в наборе символов ASCII. Например, шаблон '0'..'9' соответствует всем символам, которые являются цифрами.
# type char_class = Uppercase | Lowercase | Digit | Other
let classify_char = function
| 'A'..'Z' -> Uppercase
| 'a'..'z' -> Lowercase
| '0'..'9' -> Digit
| _ -> Other;;
type char_class = Uppercase | Lowercase | Digit | Other
val classify_char : char -> char_class = Ленивые шаблоны
(Введены в Objective Caml 3.11)
|
Шаблон lazy шаблон соответствует значению v типа Lazy.t, при условии, что шаблон соответствует результату принудительного вычисления v с помощью Lazy.force. Успешное сопоставление шаблона, содержащего подшаблоны lazy, принуждает соответствующие части сопоставляемого значения, даже те, которые не подразумевают проверки, такие как lazy имя-значения или lazy _. Сопоставление значения с сопоставлением-шаблонов, где некоторые шаблоны содержат подшаблоны lazy, может подразумевать принудительное вычисление частей значения, даже если выбранный в конечном итоге шаблон не содержит подшаблона lazy.
# let force_opt = function
| Some (lazy n) -> n
| None -> 0;;
val force_opt : int lazy_t option -> int = Для получения дополнительной информации см. описание модуля Lazy в стандартной библиотеке (модуль Lazy).
Шаблоны исключений
(Введены в OCaml 4.02)
Новый вид шаблона исключений, exception шаблон , разрешен только как шаблон верхнего уровня или внутри шаблона «или» верхнего уровня под match...with (другие вхождения отклоняются проверкой типов).
Случаи с таким шаблоном верхнего уровня называются «случаями исключений», в отличие от обычных «случаев значений». Случаи исключений применяются, когда вычисление сопоставляемого выражения вызывает исключение. Затем значение исключения сопоставляется со всеми случаями исключений и повторно поднимается, если ни один из них не принимает исключение (как и в блоке try...with). Поскольку тела всех случаев исключений и случаев значений находятся вне области обработчика исключений, они все считаются находящимися в хвостовой позиции: если сам блок match...with находится в хвостовой позиции в текущей функции, любой вызов функции в хвостовой позиции в одном из тел случая приводит к фактическому хвостовому вызову.
В сопоставлении шаблонов должен быть по крайней мере один случай значения. Ошибка, если все случаи являются исключениями, так как не будет кода для обработки возврата значения.
# let find_opt p l =
match List.find p l with
| exception Not_found -> None
| x -> Some x;;
val find_opt : ('a -> bool) -> 'a list -> 'a option = Локальные открытия для шаблонов
(Введены в OCaml 4.04)
Для шаблонов локальные открытия ограничены конструкцией путь-модуля.(шаблон). Эта конструкция локально открывает модуль, на который ссылается путь к модулю путь-модуля, в области действия шаблона шаблон.
Когда тело локального открытого шаблона ограничено [ ], [| |] или { }, скобки можно опустить. Например, module-path.[pattern] эквивалентно module-path.([pattern]), а module-path.[| pattern |] эквивалентно module-path.([| pattern |]).
# let bytes_allocated Gc.{minor_words; major_words; promoted_words; _} =
(Sys.word_size / 4)
* int_of_float (minor_words +. major_words -. promoted_words);;
val bytes_allocated : Gc.stat -> int =
© 1995-2022 INRIA.
https://v2.ocaml.org/releases/4.14/htmlman/patterns.html