第 11 章 OCaml 语言

6 模式

模式::= 值名
 _
 常量
 模式as值名
 (模式)
 (模式:类型表达式)
 模式|模式
 构造器模式
 `标签名模式
 #类型构造器
 模式 { ,模式 }+
 {字段 [:类型表达式] [=模式]{ ;字段 [:类型表达式] [=模式] } [;_ ] [ ; ] }
 [模式 { ;模式 } [ ; ] ]
 模式::模式
 [|模式 { ;模式 } [ ; ] |]
 字符字面量..字符字面量
 lazy模式
 exception模式
 模块路径.(模式)
 模块路径.[模式]
 模块路径.[|模式|]
 模块路径.{模式}

另请参阅以下语言扩展:一等模块属性扩展节点

下表显示了运算符和非封闭模式构造的相对优先级和结合性。优先级较高的构造放在前面。

运算符结合性
..
lazy(参见第 ‍11.6 节)
构造器应用,标签应用
::
,
|
as

模式是模板,允许选择给定形状的数据结构,并将标识符绑定到数据结构的组件。此选择操作称为模式匹配;其结果要么是“此值与此模式不匹配”,要么是“此值与此模式匹配,导致将名称绑定到值的以下绑定”。

变量模式

由值名组成的模式匹配任何值,将名称绑定到该值。模式 _ 也匹配任何值,但不绑定任何名称。

# let is_empty = function | [] -> true | _ :: _ -> false;;
val is_empty : 'a list -> bool = <fun>

模式是线性的:给定模式不能多次绑定一个变量。特别是,无法仅使用模式来测试数据结构的两个部分之间的相等性

# let pair_equal = function | x, x -> true | x, y -> false;;
错误:变量 x 在此匹配中被绑定多次

但是,我们可以为此目的使用 when 保护

# let pair_equal = function | x, y when x = y -> true | _ -> false;;
val pair_equal : 'a * 'a -> bool = <fun>

常量模式

由常量组成的模式匹配等于此常量的值。

# let bool_of_string = function | "true" -> true | "false" -> false | _ -> raise (Invalid_argument "bool_of_string");;
val bool_of_string : string -> bool = <fun>

别名模式

模式 模式1 as 值名模式1 匹配相同的值。如果针对 模式1 的匹配成功,则除了针对 模式1 的匹配执行的绑定之外,名称 值名 还绑定到匹配的值。

# let sort_pair ((x, y) as p) = if x <= y then p else (y, x);;
val sort_pair : 'a * 'a -> 'a * 'a = <fun>

带括号的模式

模式 ( 模式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 = <fun>

“或”模式

模式 模式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 = <fun>

变体模式

模式 构造器 ( 模式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 = <fun>

模式 pattern1 :: pattern2 匹配非空列表,其头部匹配 pattern1,尾部匹配 pattern2

模式 [ pattern1 ;; patternn ] 匹配长度为 n 的列表,其元素分别匹配 pattern1patternn。此模式的行为类似于 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 = <fun>

多态变体模式

模式 `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 = <fun>

多态变体缩写模式

如果定义了类型 [('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 = <fun>

元组模式

模式 pattern1 ,, patternn 匹配 n 元组,其组件匹配模式 pattern1patternn。也就是说,该模式匹配元组值 (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 = <fun>

记录模式

模式 { field1 [= pattern1] ;; fieldn [= patternn] } 匹配至少定义了字段 field1fieldn 的记录,并且与 fieldi 关联的值匹配模式 patterni,其中 i = 1,… , n。单个标识符 fieldk 代表 fieldk = fieldk,单个限定标识符 module-path . fieldk 代表 module-path . fieldk = fieldk。记录值可以定义比 field1fieldn 更多的字段;与这些额外字段关联的值不会被考虑用于匹配。可选地,记录模式可以以 ; _ 结尾,以传达记录类型并非所有字段都列在记录模式中,并且这是故意的。可以通过 { 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 = <fun>

数组模式

模式 [| 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 = <fun>

范围模式

模式 ' c ' .. ' d ' 是模式的简写

' c ' | ' c1 ' | ' c2 ' || ' cn ' | ' d '

其中 c1c2、…、cn 是 ASCII 字符集中 cd 之间出现的字符。例如,模式 '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 = <fun>

惰性模式

(在 Objective Caml 3.11 中引入)

模式::= ...

模式 lazy pattern 匹配类型为 Lazy.t 的值 v,前提是 pattern 匹配使用 Lazy.force 强制 v 的结果。包含 lazy 子模式的模式的成功匹配会强制匹配的值的相应部分,即使这些部分不包含任何测试,例如 lazy value-namelazy _。使用某些模式包含 lazy 子模式的 pattern-matching 匹配值可能会强制值的部分,即使最终选择的模式没有 lazy 子模式。

# let force_opt = function | Some (lazy n) -> n | None -> 0;;
val force_opt : int lazy_t option -> int = <fun>

有关更多信息,请参阅标准库中模块 Lazy 的描述(模块 Lazy)。

异常模式

(在 OCaml 4.02 中引入)

一种新的异常模式形式 exception pattern 仅允许作为顶级模式或在 match...with 模式匹配下的顶级或模式中使用(类型检查器会拒绝其他情况)。

使用此类顶级模式的 case 称为“异常 case”,与常规的“值 case”相对。当匹配表达式的计算引发异常时,会应用异常 case。然后将异常值与所有异常 case 匹配,如果没有任何 case 接受异常,则会重新引发异常(与 try...with 块一样)。由于所有异常和值 case 的主体都位于异常处理程序的作用域之外,因此它们都被视为位于尾部位置:如果 match...with 块本身位于当前函数的尾部位置,则任何 case 主体中尾部位置的函数调用都会导致实际的尾部调用。

模式匹配必须包含至少一个值 case。如果所有 case 都是异常,则会出错,因为没有代码来处理值的返回。

# 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 = <fun>

模式的本地打开

(在 OCaml 4.04 中引入)

对于模式,本地打开仅限于 module-path.(pattern) 结构。此结构在模式 pattern 的作用域中本地打开模块路径 module-path 所引用的模块。

当本地打开模式的主体由 [ ][| |]{ } 分隔时,可以省略括号。例如,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 = <fun>