Spec-Zone.ru › OCaml
☰Язык OCaml
  • Язык OCaml
  • Расширения языка

Глава 11. Язык OCaml

6 Шаблоны

pattern ::= value-name
∣ _
∣ constant
∣ pattern as value-name
∣ ( pattern )
∣ ( pattern : typexpr )
∣ pattern | pattern
∣ constr pattern
∣ `tag-name pattern
∣ #typeconstr
∣ pattern { , pattern }+
∣ { field [: typexpr] [= pattern]{ ; field [: typexpr] [= pattern] } [; _ ] [ ; ] }
∣ [ pattern { ; pattern } [ ; ] ]
∣ pattern :: pattern
∣ [| pattern { ; pattern } [ ; ] |]
∣ char-literal .. char-literal
∣ lazy pattern
∣ exception pattern
∣ module-path .( pattern )
∣ module-path .[ pattern ]
∣ module-path .[| pattern |]
∣ module-path .{ pattern }

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

В таблице ниже приведены относительный приоритет и ассоциативность операторов и незамкнутых конструкций шаблонов. Сначала указаны конструкции с более высоким приоритетом.

Оператор Ассоциативность
.. –
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 ' является сокращением шаблона

' c ' | ' c1 ' | ' c2 ' | … | ' cn ' | ' 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 = 
« КонстантыВыражения »
Copyright © 2026 Institut National de Recherche en Informatique et en Automatique

© 1995-2026 INRIA.
https://ocaml.org/manual/5.5/patterns.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API