Spec-Zone.ru › OCaml 5.0

11.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 = 

Шаблоны записей

Шаблон { поле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 ' является сокращением для шаблона

' 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 шаблон соответствует значению 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/5.0/htmlman/patterns.html

Spec-Zone.ru

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