Julia语言程序设计
上QQ阅读APP看书,第一时间看更新

4.4 比较运算符

数值类型对象之间的对比,也是常常遇到的问题。比较运算符便是对两个操作数的关系进行比较,在大于小于、是否相等或者是否完全相同等条件下给出逻辑判断,并返回一个布尔型的结果(Bool值):在给定条件为真时返回true,为假时则返回false值。因为涉及两个值,所以比较运算符都是二元的。Julia提供的比较运算符如表4-3所示。

表4-3 比较运算符

其中有些是非传统的Unicode符号,例如≠、≤及≥等,这些是在数学表述中常见的符号,但在编程语言中却是不常见的,这是Julia的特色。

1.大小比较

我们先看小于(<)及大于(>)这两个比较运算符,仍是从例子开始:


julia> a < b     # Int64(50) < Int32(2)
false

julia> a > b     # Int64(50) > Int32(2)
true

julia> c < d     # Float32(2.5) < Float64(3.0)
true

julia> a > c     # Int64(50) > Float32(2.5)
false

julia> a < y                                                             # Int64(50) < Rational{Int64}(15//8)
false

julia> c > y                                                            # Float32(2.5) > Rational{Int64}(15//8)
true

可见,比较运算符也可以对不同的操作数类型进行操作。

不过需要注意的是,大小的比较对两个复数是不适用的,例如:


julia> 3+2im < 3+1im
ERROR: MethodError: no method matching isless(::Complex{Int64}, ::Complex{Int64})

实际上,这种比较在数学上也是无意义的。

2.相等或相同

“小于”及“大于”这两种运算符是互斥的,不可能同时成立,但也有可能都不成立,因为还有第三种情况,即两者相等。我们可以使用等于(==)或者不等于(!=或≠)判断。

同样,“不等于”或“等于”这两者也是互斥的,不可能同时成立。例如:


julia> Int32(10) == Int64(10)                              # 类型不同
true

julia> UInt8(5) == Int64(5)                              # 类型不同
true

julia> 5 != 3
true

julia> Int32(15) != Int8(15)
false

julia> Int32(1) == Float64(1.0)                     # 浮点型与整型对比时,可以获得正确的结论
true

可见,是否相等的比较会忽略值的具体类型,而直接对数值本身进行比较。

另外,与大小比较不同的是,是否相等对于有理数以及复数类型是适用的,例如:


julia> 15//8 == 3
false

julia> 15//8 != 1.875                                            # 有理数15//8的实际值为1.875
false

julia> 3+1im == 3+im
true

julia> 3+2im == 1.875
false

julia> 15//8 != 3+2im
true

必要时,如果判断是否相等的同时需要考虑值的类型,则可用===这个“完全相同”运算符,例如:


julia> Int32(10) === Int64(10)               # 类型不同
false

julia> Int32(15) === Int8(15)               # 类型不同
false

julia> 3+1im === 3+im                              # 类型与值均相同
true

julia> Int32(10) === Int32(10)               # 类型与值均相同
true

如例中所示,该运算符只有在两个操作数的值与类型均相同时,才会返回true值。

运算符!==或则与之相对,表示“不全相同”,值或类型不一致便会返回true值,例如:


julia> Int32(19) === Int32(19)               # 类型与值均相同
true

julia> Int32(19) !== Int32(19)                # 类型与值均相同
false

julia> Int32(19) !== Int64(19)                # 类型不同
true

julia> Int32(19) !== Int64(10)                # 类型与值均不同
true

如果在开发中需要严格的比较,可以考虑使用该类运算符。

注意 事实上,是否相等及是否相同可以适用于两个操作数是任意类型的情况,即可以对任意两个对象使用这两类操作符,判断它们是否相同或相等。但大小比较并非如此,需要操作数是同类型,而且该类型在数学上要有意义才行。这点在开发中是需要注意的。

3.特殊值比较

上述的比较运算符对无穷值也是适用的,例如:


julia> Inf32 == Inf                                            # 不同类型的正无穷是相等的
true

julia> Inf == Inf
true

julia> Inf > Inf
false

julia> Inf < Inf
false

但对于NaN这个特殊的浮点值,比较结果却相当特别,如下:


julia> NaN == NaN
false

julia> NaN > NaN
false

julia> NaN < NaN
false

julia> NaN != NaN
true

无论是大于、小于还是等于均不能成立。

这一点可以从NaN的本质意义上去理解。NaN设立的目的是表达一种无效值,而无效值本质上并非是一个具体的值,所以无从进行比较。另外,在将Inf或普通数值与NaN进行比较时,也遵循这个原则,例如:


julia> Inf > NaN
false

julia> Inf == NaN
false

julia> Inf < NaN
false

julia> NaN != Inf
true

实际上,在对无穷值与NaN在内的操作数进行比较时,Julia遵循IEEE 754标准[1]的约定:

·正无穷大等于自身,但大于除了NaN外的任何数值。

·负无穷大等于自身,但小于除了NaN外的任何数值。

·NaN不等于、不小于也不大于任何数,包括自身。

·正零等于但不大于负零。

但在实际使用中,往往希望将同为NaN的两个值作为同一事物进行处理,或者需要区分“正零”与“负零”,则可使用isequal()函数。通常情况下,该函数等效于==这个运算符,仅在值为NaN或零时会有所不同。例如:


julia> isequal(NaN, NaN)
true

julia> isequal(NaN, NaN32)
true

julia> -0.0 == 0.0 
true 

julia> isequal(-0.0, 0.0) 
false

开发者可以根据应用的需求进行选择。

4.其他比较操作

运算符“约等于”≈和“不约等于”是Julia有特色的部分。两者的用途相反,用于判断两个操作数是否在默认的阈值下满足大约相等关系。例如:


julia> 0.1 ≈ (0.1 - 1e-10)               # 两个操作数相差较小
true

julia> 1.3 ≈ 1.300000002                              #相差较小
true

julia> 1.3  1.300002                              #相差较大
true

这两个运算符在不需要精确对比的时候能够提供不少便利。

另外两个操作符,“大于等于”(>=或≥)以及“小于等于”(<=或≤),则可同时对上述的两种情况进行判断。但同样,这两类运算符不适用于复数。至于这两类运算符的用法,因与大小比较相似,不再赘述。

此外,对于多个操作数的比较,Julia还提供了链式比较表达式(Chaining Comparisons),例如:


julia> 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5 
true

不过由于可读性较差,笔者不建议使用,也不再过多介绍,有兴趣的读者可参见官方文档进一步了解。

[1] IEEE 754标准是二进位浮点数算术标准(IEEE Standard for Floating-Point Arithmetic)的编号,等同于国际标准ISO/IEC/IEEE 60559。该标准由美国电气电子工程师学会(IEEE)计算机学会旗下的微处理器标准委员会(Microprocessor Standards Committee, MSC)发布。