type foo = A of int * int | B of (int * int)
int * int
和有什么区别(int * int)
?我看到的唯一区别是模式匹配:
let test_foo = function
| A (f, s) -> (f, s)
| B b -> b
它只是语法糖吗?你如何选择使用哪一个?这两种形式有什么性能差异吗?
type foo = A of int * int | B of (int * int)
int * int
和有什么区别(int * int)
?我看到的唯一区别是模式匹配:
let test_foo = function
| A (f, s) -> (f, s)
| B b -> b
它只是语法糖吗?你如何选择使用哪一个?这两种形式有什么性能差异吗?
是的,存在性能差异:
在内存A (23, 42)
中将包含一个将其标识为 a 的标签A
和两个整数 23 和 42。B (23, 42)
将包含一个将其标识为 a 的标签B
和一个指向包含整数23
和的元组的指针42
。因此,在创建 a 时将有一个额外的内存分配,B
而在访问 a 中的各个值时会有一个额外的间接级别B
。因此,在您实际上不将构造函数参数用作元组的情况下, usingA
将比 using 涉及更少的开销B
。
另一方面,您的test_foo
函数每次使用A
值调用时都会创建一个新元组,但是当使用值调用它时,B
它只会返回内存中已经存在的元组。所以test_foo
是一个B
比它便宜的操作A
。因此,如果您将构造函数的参数用作元组,并且您将多次使用相同的值,那么使用B
会更便宜。
因此,如果您要将构造函数参数用作元组,那么使用带元组的构造函数是有意义的,因为您可以使用模式匹配用更少的代码获取元组,并且因为它可以避免必须从多次相同的值。在所有其他情况下,最好不要使用元组,因为它涉及更少的内存分配和更少的间接性。
如前所述,构造函数A
采用两个int
,而构造函数B
采用有序对。
所以你可以写
let bar = A (1, 2)
或者
let bar = B (1, 2)
或者
let bar = (1, 2)
let baz = B bar
但你不能写
let bar = (1, 2)
let baz = A bar
此外,在您的模式匹配中,您仍然可以将 B 的内容匹配为两个 int,但您不能将 A 的内容匹配为绑定到有序对的值
let test_foo = function
| A a -> a (* wrong *)
| B (f, s) -> (f, s) (* ok *)
它们是两种不同的类型。这种语法的解释在*
运算符处是模棱两可的。它可以简化为以下形式:
type x = Y * Z
其中 '*' 与type
OCaml
int * int
中的关键字相关联,或者其中*
用于构造元组的运算符的容量
默认优先级为前者。通过在 周围加上括号,(int * int)
您可以覆盖默认优先级并强制执行后一种解释。
这是 OCaml 语法中的棘手问题之一——即使看起来您正在声明一个具有元组数据类型 ( A of int * int
) 的构造函数,并且即使当您使用构造函数时,看起来您正在给它一个元组 ( A (2,3)
),这实际上并不是正在发生的事情。
如果你实际构造了一个元组值并尝试将它传递给构造函数,它将无法编译 -- let x = (2,3) in A x
。相反,*
构造函数中的定义和(,)
构造函数中的 use 表达式只是多参数构造函数的语法。该语法模仿带有元组参数的构造函数,但实际上是分开的。如果您想实际使用单个元组参数创建构造函数,则额外的括号是必需的。