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 = 

Шаблоны в скобках

Шаблон ( шаблон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 ' — это сокращение для шаблона

' 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 = 
« КонстантыВыражения »
Авторское право © 2024 Institut National de Recherche en Informatique et en Automatique

© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/patterns.html

Spec-Zone.ru

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