6 模式
另請參閱以下語言擴展:第一類模組、屬性 和 擴展節點。
下表顯示運算子和非封閉模式結構的相對優先順序和結合性。優先順序較高的結構會先出現。
運算子 | 結合性 |
.. | – |
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;;
Error: 變數 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,則執行的繫結是 v 匹配 模式1 時的 模式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>
變體模式
模式 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 = <fun>
模式 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 = <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 個元件分別匹配模式 pattern1 到 patternn 的 n 元組。也就是說,模式匹配元組值 (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] } 匹配定義了至少 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 = <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 '
其中 c1, c2, …, cn 是在 ASCII 字元集中介於 c 和 d 之間的字元。例如,模式 '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-name 或 lazy _。使用 pattern-matching 匹配值,其中某些模式包含 lazy 子模式,可能會導致強制執行值的某些部分,即使最終選擇的模式沒有 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 模式匹配下的頂層或模式內部使用(其他情況會被類型檢查器拒絕)。
具有這種頂層模式的情況被稱為「例外情況」,與常規的「值情況」相對。當匹配的運算式引發例外時,將會應用例外情況。然後,例外值會與所有例外情況進行匹配,如果沒有任何例外情況接受該例外,則會重新引發該例外(就像 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 = <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>