1、操作定义
1.1 ===
= = =操作符(一个别名是函数)实现了Henry Baker's的公平谓词(EGAL)参考文献[1, 2] :x = = = y是真的当两个对象编程无法区分,即你不能通过代码来演示了x和y之间的任何差异。这可以归结为以下规则,
这些规则递归地定义了===的行为:
(1)对于可变值(数组、可变组合类型),===检查对象标识:如果x和y是相同的对象,并且存储在内存中的相同位置,则x === y为真。
(2)对于不可变的组合类型,如果x和y具有相同的类型—因此具有相同的结构—并且它们的相应子类型,都递归地 可以用 ===验证为真,则x === y为真。
(3)对于位类型(不可变的数据块,如Int或Float64),如果x和y包含完全相同的位,则x === y为真。
1.2 ==
== 函数是用户可定义的,并且实现了“抽象值相等”。重载 是一个与===的关键区别:
(1)=== 不可重载-它是一个内建函数,具有固定的预定义行为。您不能扩展或更改它的行为。
(2)== 是可重载的——它是一个普通的(对于Julia来说)带有中缀语法的泛型函数。它有可回退定义,这使它在用户定义的类型上具有一些默认的行为,但是也可以通过为 == 添加新的、更具体的方法来修改它的行为。
要获得更多关于==如何在内置类型中工作的细节,以及扩展它时,应该如何在用户定义类型定义它的行为,可以参考文档(https://docs.julialang.org/en/v1.3/)
3、关于数值的比较
所有数值类型都通过数值进行比较,忽略类型。字符串作为字符序列进行比较,忽略编码。
你可以把这看作是“直觉上的平等”。如果两个数字在数字上相等,它们是==:
1 == 1.0 == 1 + 0im == 1.0 + 0.0im == 1//1
#true
0.5 == 1/2 == 1//2
#true
注意,==实现了精确的数值等式:
2/3 == 2//3
#false
这些值是不相等的,因为2/3是浮点值0.6666666666666666,这是与数学值2/3最接近的浮点值(或者用Julia notation表示一个有理数,2//3),但是0.6666666666666666并不完全等于2/3。此外,== 遵循IEEE 754浮点数语义。
这包括一些可能意想不到的地方:
(1)浮点型零(0.0和-0.0)有明确的正、负浮点型零(0.0和-0.0):它们是== 值相等,但是它们的行为不同,因此===不是相等的
(2) 有许多不同的非数字(NaN)值:它们与自己、彼此或任何其他值使用==比较都是不相等的;但是与自己使用 === 比较是相等的。
下面是一些例子:
0.0 === -0.0 #false
0.0 == -0.0 #true
1/0.0 #Inf
1/-0.0 #-Inf
NaN === NaN #true
NaN === -NaN #false
-NaN === -NaN #true
NaN == NaN #false
NaN == -NaN #false
NaN == 1.0 #false
4、集合比较
集合通常应该通过对所有内容递归的调用==比较,来实现集合的==的比较功能。由==给出的值相等的概念被递归地扩展到集合中:
[1, 2, 3] == [1, 2, 3] #true
[1, 2, 3] == [1.0, 2.0, 3.0] #true
[1, 2, 3] == Any[1//1, 2.0, 3 + 0im] #true
这也有它的缺点 比如:a与自身的比较为false
a = [1, NaN, 3]
# 3-element Array{Float64,1}:
# 1.0
# NaN
# 3.0
a == a
#false
另一方面,===比较总是测试对象标识(objectid),因此即使两个数组具有相同的类型并包含相同的值,它们只有指向同一个数组才会===相等:
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
a和b不===相等的原因是,尽管它们当前恰好包含相同的数据,但由于它们是可变的且没有指向相同的对象,因此可以对其中一个进行修改,而不影响另外一个,所以它们是不同的。