ocaml中"等于(=)"和"相同(==)"之间的区别是什么?

Jac*_*ale 27 ocaml functional-programming

OCaml,我们有两种equity comparisons:

x = y而且x == y,

那么它们之间的区别究竟是什么呢?

x = y是ocaml就像x.equals(y)在Java中一样吗?

并且x == y就像x == y (comparing the address)在Java中?

Jef*_*eld 29

我不确切知道x.equals(y)Java中的工作原理.如果进行"深度"比较,则类比非常接近.需要注意的一点是,物理平等是OCaml(以及一般的功能语言)中的一个滑稽概念.编译器和运行时系统将移动值,并且可以随意合并和取消合并纯(非可变)值.所以你应该只==在你真正了解自己在做什么的时候使用.在某种程度上,它需要熟悉实现(除非必要,这是必须避免的).

OCaml所做的具体保证==很薄弱.可变值的比较方式与您期望的方式相同(即,如果改变其中一个实际上也会改变另一个).但是对于非可变值,唯一的保证是物理上等于(==)的值也将比较为equal(=).请注意,反过来不是真的如sepp2k指出了浮动值.

从本质上讲,语言规范告诉您的非可变值的是,您可以使用==快速检查来确定两个非可变值是否相等(=).如果他们比较物理上相等,那么他们在价值方面是相同的.如果他们不比较物理上的平等,你就不知道他们是否具有相同的价值.你还是要=用来决定.

  • 我想对使用`==`作为快速路径的建议添加一个强烈的警告,因为运行时已经在大多数情况下在语义上正确的情况下执行了此操作.我认为手动执行它的唯一情况是非平面数据结构,你碰巧知道内部没有浮动,没有函数和抽象标记,但编译器却没有. (3认同)

did*_*erc 18

编辑:这个答案深入研究了基于Obj模块的OCaml内部工作的细节.这些知识并不是在没有特别注意的情况下使用的(让我再一次强调这个非常重要的一点:不要将它用于你的程序,但只有当你想要试验 OCaml运行时).这些信息也可以获得,尽管可能是O'Reilly关于OCaml的书中更容易理解的形式,可在线获得(相当好的书,虽然现在有点过时).

=操作者检查结构相等,而==只检查物理平等.

等式检查基于值分配和存储在内存中的方式.OCaml中的运行时值可大致适合2种不同的类别:盒装或未装箱.前者意味着通过间接在内存中可以访问该值,后者意味着可以直接访问该值.

由于int(32位系统上的int31或64位系统上的int63)是未装箱的值,因此两个运算符的行为都相同.实际上,其运行时实现的一些其他类型或值int也将看到两个运算符与它们的行为相同,例如单位(),空列表[],代数数据类型的常量和多态变体等.

一旦你开始玩更复杂的值涉及结构,如列表,数组,元组,记录(C结构等价),这两个运算符之间的差异出现:结构中的值将被加框,除非它们可以运行时表示为本机int (1).这种必要性源于运行时系统必须如何处理值并有效管理内存.当从其他值构造时分配结构化值,其他值本身可以是结构化值,在这种情况下使用引用(因为它们是盒装的).

由于分配,在程序的不同点实例化的两个值在物理上不相同的情况下是不太可能的,尽管它们在结构上是相等的.值中的每个字段或内部元素可以是相同的,甚至达到物理标识,但如果这两个值是动态构建的,那么它们最终会在内存中使用不同的空间,因此在物理上是不同的,但在结构上是相同的.

运行时尝试避免不必要的分配:例如,如果你有一个函数返回总是相同的值(换句话说,如果函数是常量),无论是简单的还是结构化的,该函数将始终返回相同的物理值(即,内存中的相同数据),以便测试物理相等,该函数的两次调用的结果将是成功的.

观察物理运算符何时实际返回的一种方法trueObj.is_block在其运行时表示上使用该函数(也就是说,它的结果Obj.repr).此函数只是告诉它的参数运行时表示是否已装箱.

更人为的方法是使用以下功能:

let phy x : int = Obj.magic (Obj.repr x);;
Run Code Online (Sandbox Code Playgroud)

如果此值被加框,则此函数将返回int指向x内存中绑定值的指针的实际值.如果您在文字上尝试它,您将获得完全相同的值!那是因为int是未装箱的(即,值直接存储在内存中,而不是通过引用).int

现在我们知道盒装值实际上是"引用"值,我们可以推断出这些值可以修改,即使语言表明它们是不可变的.

例如考虑引用类型:

# type 'a ref = {mutable contents : 'a };;
Run Code Online (Sandbox Code Playgroud)

我们可以像这样定义一个不可变的引用:

# type 'a imm = {i : 'a };;
type 'a imm = {i : 'a; }
Run Code Online (Sandbox Code Playgroud)

然后使用该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 }
Run Code Online (Sandbox Code Playgroud)

这有一些例外:

  • 如果值是对象,那么即使看似结构相同的值也会false在结构比较中返回

    # let o1 = object end;;
    val o1 : < > = <obj>
    # let o2 = object end;;
    val o2 : < > = <obj>
    # o1 = o2;;
    - :  bool = false
    # o1 = o1;;
    - :  bool = true
    
    Run Code Online (Sandbox Code Playgroud)

    在这里,我们看到它=恢复到物理对等.

  • 如果值是函数,则无法在结构上对它们进行比较,但物理比较可以按预期工作.

  • 懒惰值可能会也可能不会在结构上具有可比性,这取决于它们是否被强制(分别).

    # 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 
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果模块或记录值不包含任何功能值,则它们也是可比较的.

一般来说,我猜可以安全地说,与函数相关的值,或者可能在内部保存函数的值不可比较=,但可以与之比较==.

显然你应该对这一切非常谨慎:依赖于运行时的实现细节是不正确的(注意:我在我的答案的初始版本中开玩笑地使用了邪恶这个词,但是由于担心它被认真对待而改变了它).正如你在评论中恰当地指出的那样,javascript实现的行为对于浮点数是不同的(在javascript中结构上相同,但在参考实现中没有,以及java的那个?).


(1)如果我没记错的话,浮点数在存储在数组中时也会被取消装箱以避免双重间接,但是一旦提取它们就会变成盒装,所以你不应该看到盒装值的行为差异.

  • "`让phy x:int = Obj.magic(Obj.repr x);;`将返回一个`int`":这是一个常见的误解,但事实并非如此.它返回一个类型为"int"的值,其运行时表示未更改,因此没有设置其最低位.如果你应用一个读取该位的函数(例如`hash`或者在某些情况下,`(=)`),那么该函数的行为与分配的值一样.如果你应用一个算术`int`函数,它可以在不读取最低位的情况下工作,你将得到一个与初始分配值的地址相关的`int` ... (4认同)
  • 如果你应用一个算术`int`函数,**假设**它的参数的最低位被设置,你可能会得到一个无效的指针,GC很快就会崩溃.第一种算术函数的例子是`(lor)0`,第二种算术函数通常是`succ`. (4认同)
  • 以上两条评论仅仅是为什么在回答初学者提出的OCaml问题时提及模块"Obj"是一个完全不好的想法的例子. (3认同)

sep*_*p2k 8

ocam中的x = y就像Java中的x.equals(y)一样吗?

和x == y就像Java中的x == y(比较地址)?

对,就是那样.除了在OCaml中,您可以使用=各种值,而在Java中,您不能equals在原始类型上使用.另一个区别是OCaml中的浮点数是引用类型,因此您不应该使用它们进行比较==(并不是说通常最好直接比较浮点数是否相等).

总而言之,您基本上应该始终使用=比较任何类型的值.