【Janet】比较运算符
比较运算符用于比较值来建立相等和顺序。Janet 有两种比较运算符,分别叫做原始比较和多态比较。
原始比较运算符
原始比较运算符是 =, <, <=, >, >=。简单来说,每个运算符都可以用来比较两个值:如果 a < b, (< a b) 会返回真,否则返回假。同样 (= a b) 仅当 a==b 时返回真。
(= 3 3) # true
(< 1 3) # true
(>= :a :a) # true
(> "bar" "foo") # false -- strings compare lexicographically
(<= :bar :foo) # true -- keywords compare lexicographically by keyword name
(= nil nil) # true -- nil always equal to itself and only itself
更一般的,每个运算符都支持任意数量的参数(包括0),只要参数没有违反操作符定义的顺序就返回真。因此 (< 1 2 3) 返回真,但是 (> 3 2 4) 返回假。做为一个极端, (<) 返回真,因为它的(不存在的)参数没有违反它的顺序。
(= 1 1 1) # true
(< 1 3 5) # true
(>= 3 1 7) # false
(> 1) # true
原始比较运算符为所有的 Janet 类型提供了一个总的排序,但是不同类型的值的比较结果可能会出乎你的意料。如果原始比较运算符的两个参数是不同类型的,它们会根据 Janet 内部的类型数字排序。这可能并不是用户所需要的,例如,比较 Janet int/s64 类型和 Janet 数字类型。
# surprisingly evaluates to false:
(= (int/s64 1) (int/u64 1))
# surprisingly evaluates to true but this due to Janet internal type number
# for int/u64 types being greater than the internal type number for numbers!
(< 3 (int/u64 2))
如果你不想根据类型数字比较不同类型(例如比较数值),可以使用下面的多态比较运算符。
多态比较运算符
多态比较运算符用来以其他方式比较不同类型而不是类型码。其语义由比较的类型决定。它的目的是以更正确的方式(相比于原始比较运算符)比较具有自然顺序的两个类型。
多态比较运算符包括 compare=, compare<, compare<=, compare>, compare>=。它们的用法与原始比较运算符相同:
(compare< 1 3) # true
(compare> "bar" "foo") # false -- strings compare lexicographically
(compare<= :bar :foo) # true -- keywords compare lexicographically by keyword name
(compare= nil nil) # true -- nil always equal to itself and only itself
(compare< 1 2 3) # true -- just like <
当他用来比较 int/s64, int/u64 和数字类型时,会得到正确的结果。
(compare= (int/s64 1) (int/u64 1)) # true -- they are "semantically" equal
(compare< 3 (int/u64 2)) # false -- semantically 3 is not < 2
通常多态运算符要比原始运算符慢,因此优先使用原始比较运算符,除非需要多态特性。
实现多态比较
如果只是将多态比较用于内置类型,可以跳过本章,本章将介绍如何为自定义类型实现多态比较。
多态比较使用 compare 函数来比较 Janet 值。对于 a<b, a=b, a>b compare 函数分别返回 -1,0 和 1。比较运算符,如 compare< 根据这个结果返回真或假(多参数情况会多次调用 compare)。 compare 函数的算法如下:
- 如果
a实现了:compare且(:compare a b)不是空,则返回(:compare a b)。 - 如果
b实现了:compare且(:compare b a)不是空,则返回(- (:compare a b))。 - 其他情况返回
(cond (< a b) -1 (= a b) 0 1)。
compare 方法最终会使用原始比较做为兜底,多态比较会产生 Janet 类型的全局顺序。
抽象类型的比较方法可以使用 C 实现,基于表的对象的 compare 函数可以使用 Janet 实现。
深度相等操作符
原始 = 操作符不会比较可变数据结构(数组,表和缓冲区)的内容。而是判断连个值是否是同一个对象。
# Even though they have the same contents these are considered *not* equal
(assert (not= @{:a 1} @{:a 1}))
(assert (not= @[:a :b] @[:a :b]))
(assert (not= @"abc" @"abc"))# primitive equal will return true if they are same object
(let [x @{:a 1}](assert (= x x)))# NOTE: = works as expected on immutable structures
(assert (= [:a :b] [:a :b]))
(assert (= {:a 1} {:a 1}))
(assert (= "abc" "abc"))
要比较可变数据结构的内容,使用 deep= 函数。这个函数像比较字符串一样比较缓冲区,并且递归地比较数组和表。
(assert (deep= [:a :b] [:a :b]))
(assert (deep= {:a 1} {:a 1}))
(assert (deep= "abc" "abc"))(assert (deep=@{ :x @[@{:a 1} 3 @"hi"] :y 2 }@{ :x @[@{:a 1} 3 @"hi"] :y 2 }))# NOTE deep= uses primitive = when the values are not an array, table or buffer.
(assert (deep-not= @{ :x (int/s64 1) } @{ :x (int/u64 1) }))
