在OCaml
中,我们有两种equity comparisons
:
x = y
和x == y
,
那么它们之间的确切区别是什么?
在 ocaml 中是不是x = y
就像x.equals(y)
在 Java 中一样?
x == y
就像x == y (comparing the address)
在Java中一样?
在OCaml
中,我们有两种equity comparisons
:
x = y
和x == y
,
那么它们之间的确切区别是什么?
在 ocaml 中是不是x = y
就像x.equals(y)
在 Java 中一样?
x == y
就像x == y (comparing the address)
在Java中一样?
我不知道x.equals(y)
在 Java 中是如何工作的。如果它进行“深度”比较,那么类比非常接近。需要注意的一件事是,物理平等是 OCaml(以及一般的函数式语言)中的一个模糊概念。编译器和运行时系统将移动值,并且可以随意合并和取消合并纯(非可变)值。所以你应该只==
在你真的知道你在做什么的情况下使用。在某种程度上,它需要熟悉实现(除非必要,否则应避免这样做)。
OCaml 做出的具体保证==
很弱。可变值以您期望的方式比较物理上相等(即,如果改变两者中的一个实际上也会改变另一个)。但是对于非可变值,唯一的保证是物理上比较相等的值 ( ==
) 也将比较相等 ( =
)。请注意,反之亦然,因为sepp2k 指出浮点值。
本质上,对于非可变值,语言规范告诉您的是,您可以将==
其用作快速检查来确定两个非可变值是否相等(=
)。如果它们在物理上比较相等,那么它们在价值方面是相等的。如果它们在物理上不相等,您将不知道它们在价值方面是否相等。你还是得=
用来决定。
Obj
编辑:这个答案基于模块深入探讨了 OCaml 内部工作的细节。这些知识不应该在没有额外注意的情况下使用(让我再次强调这个非常重要的一点:不要将它用于您的程序,但前提是您希望尝试使用 OCaml 运行时)。该信息也可用,尽管在 O'Reilly 关于 OCaml 的书中可能以更易于理解的形式,可在线获取(相当不错的书,虽然现在有点过时了)。
=
运算符正在检查结构相等性,而仅==
检查物理相等性。
相等检查基于值在内存中的分配和存储方式。OCaml 中的运行时值可能大致适合 2 个不同的类别:装箱或未装箱。前者意味着该值在内存中可以通过间接访问,后者意味着该值可以直接访问。
由于int
(32 位系统上的 int31 或 64 位系统上的 int63)是未装箱的值,因此两个运算符对它们的行为相同。一些其他类型或值,其运行时实现实际上是int
,也将看到两个运算符的行为与它们相同,例如 unit ()
、空列表[]
、代数数据类型中的常量和多态变体等。
一旦你开始使用涉及结构的更复杂的值,比如列表、数组、元组、记录(C 结构等价物),这两个运算符之间的区别就出现了:结构中的值将被装箱,除非它们可以在运行时表示为原生整数(1)。这种必要性源于运行时系统必须如何处理值,并有效地管理内存。当从其他值构造时分配结构化值,这些值本身可能是结构化值,在这种情况下使用引用(因为它们被装箱)。
由于分配,在程序的不同点实例化的两个值不太可能在物理上相等,尽管它们在结构上是相等的。值中的每个字段或内部元素可以相同,甚至可以是物理标识,但是如果这两个值是动态构建的,那么它们最终将使用内存中的不同空间,因此在物理上是不同的,但在结构上是相等的.
运行时会尝试避免不必要的分配:例如,如果您有一个函数总是返回相同的值(换句话说,如果函数是常量),无论是简单的还是结构化的,该函数将始终返回相同的物理值(即,内存中的相同数据),以便测试物理相等性,该函数的两次调用的结果将成功。
观察物理运算符何时实际返回的一种方法true
是在其运行时表示上使用该Obj.is_block
函数(也就是说,Obj.repr
它的结果)。这个函数只是告诉它的参数运行时表示是否被装箱。
一种更人为的方法是使用以下函数:
let phy x : int = Obj.magic (Obj.repr x);;
如果该值被装箱,则此函数将返回一个指向内存中int
绑定的值的指针的实际值。如果您在文字上尝试它,您将获得完全相同的值!那是因为 int 没有装箱(即,值直接存储在内存中,而不是通过引用)。x
int
现在我们知道装箱值实际上是“引用”值,我们可以推断这些值可以修改,即使语言说它们是不可变的。
例如考虑引用类型:
# type 'a ref = {mutable contents : 'a };;
我们可以像这样定义一个不可变的 ref:
# type 'a imm = {i : 'a };;
type 'a imm = {i : 'a; }
然后使用该Obj.magic
函数将一种类型强制转换为另一种类型,因为在结构上,这些类型将被简化为相同的运行时表示。
例如:
# let x = { i = 1 };;
- : val x : int imm = { i = 1 }
# let y : int ref = Obj.magic x;;
- : val y : int ref = { contents = 1 }
# y := 2;;
- : unit = ()
# x
- : int imm = { i = 2 }
有几个例外:
如果值是对象,那么即使看似结构相同的值也会false
在结构比较中返回
# let o1 = object end;;
val o1 : < > = <obj>
# let o2 = object end;;
val o2 : < > = <obj>
# o1 = o2;;
- : bool = false
# o1 = o1;;
- : bool = true
在这里,我们看到它=
恢复到物理等效。
如果值是函数,则无法在结构上比较它们,但物理比较可以按预期进行。
惰性值在结构上可能具有可比性,也可能不具有可比性,具体取决于它们是否被强制(分别)。
# let l1 = lazy (40 + 2);;
val l1 : lazy_t = <lazy>
# let l2 = lazy (40 + 2);;
val l2 : lazy_t = <lazy>
# l1 = l2;;
Exception: Invalid_argument "equal: functional value".
# Lazy.force l1;;
- : int = 42
# Lazy.force l2;;
- : int = 42
# l1 = l2;;
- : bool = true
如果模块或记录值不包含任何功能值,它们也是可比较的。
一般来说,我想可以肯定地说,与函数相关的值,或者内部可能包含函数的值不能与 比较=
,但可以与 比较==
。
您显然应该对所有这些都非常谨慎:依赖运行时的实现细节是不正确的(注意:我在该答案的初始版本中开玩笑地使用了邪恶这个词,但由于担心它被过于认真而改变了它)。正如您在评论中恰当地指出的那样,javascript 实现的行为对于浮点数是不同的(在 javascript 中结构等效,但在参考实现中不一样,那么 java 呢?)。
ocaml 中的 x = y 就像 Java 中的 x.equals(y) 一样吗?
和 x == y 就像 Java 中的 x == y (比较地址)一样?
对,就是那样。除了在 OCaml 中您可以使用=
各种值,而在 Java 中您不能equals
在原始类型上使用。另一个区别是 OCaml 中的浮点数是引用类型,因此您不应该使用它们来比较它们==
(并不是说直接比较浮点数是否相等通常是个好主意)。
所以总而言之,你基本上应该总是=
用来比较任何类型的值。
根据http://rigaux.org/language-study/syntax-across-languages-per-language/OCaml.html,==
检查浅层相等,并=
检查深层相等