Глава 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 = Шаблоны в скобках
Шаблон ( шаблон1 ) соответствует тем же значениям, что и шаблон1. Ограничение типа может появиться в скобочном шаблоне, как в ( шаблон1 : типвыражение ). Это ограничение требует, чтобы тип шаблона1 был совместим с типвыражением.
# let int_triple_is_ordered ((a, b, c) : int * int * int) =
a <= b && b <= c;;
val int_triple_is_ordered : int * int * int -> bool = Шаблоны «или»
Шаблон шаблон1 | шаблон2 представляет собой логическое «или» из двух шаблонов шаблон1 и шаблон2. Значение соответствует шаблону шаблон1 | шаблон2, если оно соответствует шаблону1 или шаблону2. Два подшаблона шаблон1 и шаблон2 должны привязывать идентификаторы точно к значениям с одинаковыми типами. Сопоставление выполняется слева направо. Более точно, в случае, если некоторое значение v соответствует шаблону1 | шаблону2, привязки выполняются для шаблона1, когда v соответствует шаблону1. В противном случае значение v соответствует шаблону2, привязки для которого выполняются.
# 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 = Шаблоны вариантов
Шаблон констр ( шаблон1 , … , шаблонn ) соответствует всем вариантам, конструктор которых равен констр, и чьи аргументы соответствуют шаблону1 … шаблонуn. Ошибка типа возникает, если n не равно количеству аргументов, ожидаемых конструктором.
Шаблон констр _ соответствует всем вариантам, чьей конструкцией является констр.
# 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 = Шаблон шаблон1 :: шаблон2 соответствует непустым спискам, чьи головы соответствуют шаблону1, и чьи хвосты соответствуют шаблону2.
Шаблон [ шаблон1 ; … ; шаблонn ] соответствует спискам длины n, элементы которых соответствуют шаблону1 … шаблонуn соответственно. Этот шаблон ведет себя как шаблон1 :: … :: шаблонn :: [].
# 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 = Шаблоны полиморфных вариантов
Шаблон `имя-тега шаблон1 соответствует всем полиморфным вариантам, чьей меткой является имя-тега, и чьим аргументом является шаблон1.
# 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,…)] имя-типа = [ `имя-тега1 типвыражение1 | … | `имя-тегаn типвыражениеn] определен, то шаблон #имя-типа является сокращением для следующего шаблона «или»: ( `имя-тега1(_ : типвыражение1) | … | `имя-тегаn(_ : типвыражениеn)). Он соответствует всем значениям типа [< имя-типа ].
# 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 = Шаблоны кортежей
Шаблон шаблон1 , … , шаблонn соответствует n-кортежам, чьи компоненты соответствуют шаблонам шаблон1 до шаблонn. То есть шаблон соответствует значениям кортежей (v1, …, vn) таких, что шаблонi соответствует 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-2024 INRIA.
https://ocaml.org/manual/5.2/patterns.html