Глава 11. Язык OCaml
6 Шаблоны
|
См. также следующие расширения языка: модули первого класса, атрибуты, узлы расширения, обработчики эффектов, разрешение неоднозначности литералов массивов с учётом типа и именованные кортежи.
В таблице ниже приведены относительный приоритет и ассоциативность операторов и незамкнутых конструкций шаблонов. Сначала указаны конструкции с более высоким приоритетом.
| Оператор | Ассоциативность |
| .. | – |
| lazy (см. раздел 11.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 = Шаблоны записей
Шаблон { field1 [= pattern1] ; … ; fieldn [= patternn] } соответствует записям, содержащим как минимум поля с field1 по fieldn, при этом значение, связанное с fieldi, соответствует шаблону patterni для i = 1,… , n. Один идентификатор fieldk означает fieldk = fieldk, а один квалифицированный идентификатор module-path . fieldk означает module-path . fieldk = fieldk. Запись может содержать больше полей, чем field1 …fieldn; связанные с этими дополнительными полями значения при сопоставлении не учитываются. В конце шаблона записи можно указать ; _, чтобы явно обозначить, что в шаблоне перечислены не все поля типа записи. Для каждого поля можно добавить необязательное ограничение типа, используя запись вида { field1 : typexpr1 = pattern1 ;… ;fieldn : typexprn = patternn }, чтобы потребовать совместимости типа поля fieldk с типом typexprk.
# 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 = Шаблоны массивов
Шаблон [| pattern1 ; … ; patternn |] соответствует массивам длины n, в которых элемент массива с индексом i соответствует шаблону patterni для 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 pattern соответствует значению v типа Lazy.t, если pattern соответствует результату принудительного вычисления v с помощью Lazy.force. Успешное сопоставление с шаблоном, содержащим подшаблоны lazy, приводит к принудительному вычислению соответствующих частей сопоставляемого значения, в том числе тех, которые не требуют проверки, например lazy value-name или lazy _. Сопоставление значения с помощью pattern-matching, в котором некоторые шаблоны содержат подшаблоны 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 pattern , допускается только в качестве шаблона верхнего уровня или внутри or-шаблона верхнего уровня в сопоставлении с образцом 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]), а 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-2026 INRIA.
https://ocaml.org/manual/5.5/patterns.html