"=="和"=="之间有什么区别?和"===" Julia中的比较运算符?

问题描述:

Julia中的=====比较运算符有什么区别?

What is the difference between == and === comparison operators in Julia?

@ChrisRackauckas的答案就其准确性而言是准确的-即,对于可变对象.但是,这个问题还有很多,所以在这里我将详细说明.

@ChrisRackauckas's answer is accurate as far as it goes – i.e. for mutable objects. There's a bit more to the issue than that, however, so I'll elaborate a bit here.

===运算符(is函数的别名)实现Henry Baker的EGAL谓词[ 2 ]:当两个对象在程序上无法区分时,x === y为true –即,您不能编写证明xy之间有任何区别的代码.这归结为以下规则:

The === operator (an alias for the is function) implements Henry Baker's EGAL predicate [1, 2]: x === y is true when two objects are programmatically indistinguishable – i.e. you cannot write code that demonstrates any difference between x and y. This boils down to the following rules:

  • 对于可变值(数组,可变复合类型),===检查对象身份:如果xy是同一对象,并且存储在内存中的同一位置,则x === y为true.
  • 对于不可变的复合类型,如果xy具有相同的类型(因此具有相同的结构)以及它们的相应组成部分都是递归===,则x === y为true.
  • 对于位类型(不可更改的数据块,如IntFloat64),如果xy包含完全相同的位,则x === y为true.
  • For mutable values (arrays, mutable composite types), === checks object identity: x === y is true if x and y are the same object, stored at the same location in memory.
  • For immutable composite types, x === y is true if x and y have the same type – and thus the same structure – and their corresponding components are all recursively ===.
  • For bits types (immutable chunks of data like Int or Float64), x === y is true if x and y contain exactly the same bits.

这些规则(递归应用)定义了===的行为.

These rules, applied recursively, define the behavior of ===.

另一方面,==函数是用户可定义的,并且实现了抽象值相等".超载能力是主要差异之一:

The == function, on the other hand, is user-definable, and implements "abstract value equality". Overloadability is one key difference:

  • ===不可重载-它是具有固定的预定义行为的内置函数.您不能扩展或更改其行为.
  • ==是可重载的-它是具有infix语法的常规(对于Julia)常规函数.它具有后备定义,可以为用户定义的类型提供有用的默认行为,但是您可以通过为类型添加新的更具体的方法到==来更改您认为合适的行为.
  • The === is not overloadable – it is a builtin function with fixed, pre-defined behavior. You cannot extend or change its behavior.
  • The == is overloadable – it is a normal (for Julia) generic function with infix syntax. It has fallback definitions that give it useful default behavior on user-defined types, but you can change that as you see fit by adding new, more specific methods to == for your types.

例如,所有数字类型均按数字值进行比较,而忽略 类型.将字符串作为字符序列进行比较,忽略 编码.

For example, all numeric types are compared by numeric value, ignoring type. Strings are compared as sequences of characters, ignoring encoding.

您可以将其视为直觉平等".如果两个数字在数值上相等,则它们为==:

You can think of this as "intuitive equality". If two numbers are numerically equal, they are ==:

julia> 1 == 1.0 == 1 + 0im == 1.0 + 0.0im == 1//1
true

julia> 0.5 == 1/2 == 1//2
true

请注意,但是==实现了精确的数值相等:

Note, however that == implements exact numerical equality:

julia> 2/3 == 2//3
false

这些值是不相等的,因为2/3是浮点值0.6666666666666666,该浮点值是最接近Float64的数学值2/3(或对于有理值,用Julia表示,2//3),但是0.6666666666666666不完全等于2/3.而且,==

These values are unequal because 2/3 is the floating-point value 0.6666666666666666, which is the closest Float64 to the mathematical value 2/3 (or in Julia notation for a rational values, 2//3), but 0.6666666666666666 is not exactly equal to 2/3. Moreover, ==

遵循IEEE 754浮点数的语义.

Follows IEEE 754 semantics for floating-point numbers.

这包括一些可能意外的属性:

This includes some possibly unexpected properties:

  • 有明显的正负浮点零(0.0-0.0):它们是==,即使它们的行为不同,因此也不是===.
  • 有许多不同的非数字(NaN)值:它们本身,彼此或任何其他值都不是==;它们各自彼此为===,但彼此互为!==,因为它们具有不同的位.
  • There are distinct positive and negative floating-point zeros (0.0 and -0.0): they are ==, even though they behave differently and are thus not ===.
  • There are many different not-a-number (NaN) values: they are not == to themselves, each other, or any other value; they are each === to themselves, but not !== to each other since they have different bits.

示例:

julia> 0.0 === -0.0
false

julia> 0.0 == -0.0
true

julia> 1/0.0
Inf

julia> 1/-0.0
-Inf

julia> NaN === NaN
true

julia> NaN === -NaN
false

julia> -NaN === -NaN
true

julia> NaN == NaN
false

julia> NaN == -NaN
false

julia> NaN == 1.0
false

这有点令人困惑,但这就是IEEE标准.

This is kind of confusing, but that's the IEEE standard.

此外,==的文档也指出:

集合通常应通过对所有内容递归调用==来实现==.

因此,==给出的值相等的概念被递归扩展到集合:

Thus, the notion of value equality as given by == is extended recursively to collections:

julia> [1, 2, 3] == [1, 2, 3]
true

julia> [1, 2, 3] == [1.0, 2.0, 3.0]
true

julia> [1, 2, 3] == Any[1//1, 2.0, 3 + 0im]
true

因此,这继承了标量==比较的缺点:

Accordingly, this inherits the foibles of scalar == comparisons:

julia> a = [1, NaN, 3]
3-element Array{Float64,1}:
   1.0
 NaN
   3.0

julia> a == a
false

另一方面,===比较总是测试对象的身份,因此,即使两个数组具有相同的类型并包含相同的值,它们也只有在相同的数组中才相等:

The === comparison, on the other hand always tests object identity, so even if two arrays have the same type and contain identical values, they are only equal if they are the same array:

julia> b = copy(a)
3-element Array{Float64,1}:
   1.0
 NaN
   3.0

julia> a === a
true

julia> a === b
false

julia> b === b
true

ab不是===的原因是,即使它们当前在此处恰好包含相同的数据,因为它们是可变的并且不是同一对象,所以您可以变异其中之一,然后很明显它们是不同的:

The reason that a and b are not === is that even though they currently happen to contain the same data here, since they are mutable and not the same object, you could mutate one of them and then it would become apparent that they are different:

julia> a[1] = -1
-1

julia> a # different than before
3-element Array{Int64,1}:
 -1
  2
  3

julia> b # still the same as before
3-element Array{Int64,1}:
 1
 2
 3

因此,通过突变,您可以说出ab是不同的对象.相同的逻辑不适用于不可变的对象:如果它们包含相同的数据,则只要它们具有相同的值,它们就无法区分.因此,不变值不受绑定到特定位置的困扰,这是编译器能够如此有效地优化不变值使用的原因之一.

Thus you can tell that a and b are different objects through mutation. The same logic doesn't apply to immutable objects: if they contain the same data, they are indistinguishable as long as they have the same value. Thus, immutable values are freed from the being tied to a specific location, which is one of the reasons that compilers are able to optimize uses of immutable values so effectively.

另请参见: