OCaml:作为模式匹配中的关键字表现奇怪

时间:2016-10-12 15:30:32

标签: pattern-matching ocaml

我们说我写这段代码:

# type t = A of int * int
  let f = function A (i1, i2) -> print_int i1;;
type t = A of int * int
val f : t -> unit = <fun>

完美,它有效。

现在,让我们说我有这个奇妙的功能:

# let print_couple (i1, i2) = print_int i1; print_int i2;;
val print_couple : int * int -> unit = <fun>

所以,正如您所期望的那样,我想写下以下内容

# let f = function A (_ as c) -> print_couple c;;

好吧,我不能

Error: The constructor A expects 2 argument(s),
       but is applied here to 1 argument(s)

我想知道,是因为_还是括号(我认真地对此表示怀疑,但我想要详尽无遗)?

# let f = function A _ -> failwith "Fight me"
  let g = function A (_) -> failwith "1o1";;
val f : t -> 'a = <fun>
val g : t -> 'a = <fun>

不,它不是......

哦,也许我必须展示编译器我知道我有两个参数:

# let f = function A ((_, _) as c) -> print_couple c;;
Error: The constructor A expects 2 argument(s), 
       but is applied here to 1 argument(s)

但......如果我写

# let f = function A (_, _) -> failwith "Puppey style";;

有效。那么,为什么,因为编译器知道我期待一对夫妇,我甚至试图把它给他,它一直在失败?是不是通过写A (_ as c)来命名,无论如何,第一个参数?这很奇怪,不是吗?

我的意思是,如果我写

# let rec f = function
    | [] -> ()
    | hd :: tl -> print_couple hd; f tl;;
val f : (int * int) list -> unit = <fun>

编译器不会打扰我这个列表是关联列表还是整数列表?那么从我这里得到同样的行为是很奇怪的

 # match a with
    | A c | A (_ as c) | A ((_, _) as c) -> ()

2 个答案:

答案 0 :(得分:3)

严格地说,当您声明A of int * int时,A需要两个int个参数,而不是一对int组成的参数。 _只是一个参数的占位符,因此您在编写A (_ as c)(或A _)时会看到错误消息。没有办法(没有使用黑魔法,我不会在这里描述)以某种方式将A的两个参数隐含地转换成一对:你必须诉诸match x with A (x,y) -> x,y

另一种方法是声明构造函数,使其成为一对,如下面的代码所示。请注意,这样效率较低(您需要两个间接访问A的参数,一个用于检索其中一个,另一个用于检索其组件):

type t = A of (int * int)
let print_couple (i1, i2) = print_int i1; print_int i2
let f = function A c -> print_couple c

答案 1 :(得分:2)

这是OCaml的一个怪癖,像A定义的构造函数在语法上看起来像一对,但不是一对。

# type t = A of int * int;;
type t = A of int * int
# A (3, 4);;
- : t = A (3, 4)
# let z = (3, 4) in A z;;
Error: The constructor A expects 2 argument(s),
       but is applied here to 1 argument(s)

如果将构造函数定义为实际使用一对,则可以提供一对:

# type u = B of (int * int);;
type u = B of (int * int)
# B (3, 4);;
- : u = B (3, 4)
# let z = (3, 4) in B z;;
- : u = B (3, 4)

这有点令人困惑,但它只是OCaml的工作方式。您需要为A等构造函数提供专门用括号的参数。

在与_匹配时,您不需要提供明确的括号,这也有点令人惊讶:

# let A _ = A (4, 5);;
# 

(当然,这仅作为更大模式的一部分有用。)