1. 文章
  2. 文章详情

Swift语言高级运算符(三)

前缀和后缀运算符

类与结构体也能提供标准一元运算符的实现。一元运算符只运算一个值。当运算符出现在值之前时,它就是前缀的(例如 -a),而当它出现在值之后时,它就是后缀的(例如 b!)。

要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 func 关键字之前指定 prefix 或者 postfix 修饰符:

extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        return Vector2D(x: -vector.x, y: -vector.y)
    }
}

这段代码为 Vector2D 类型实现了一元运算符(-a)。由于该运算符是前缀运算符,所以这个函数需要加上 prefix修饰符。

对于简单数值,一元负号运算符可以对它们的正负性进行改变。对于 Vector2D 来说,该运算将其 x 和 y 属性的正负性都进行了改变:

let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative 是一个值为 (-3.0, -4.0) 的 Vector2D 实例
let alsoPositive = -negative
// alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例

复合赋值运算符

复合赋值运算符将赋值运算符(=)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(+=)。在实现的时候,需要把运算符的左参数设置成 inout 类型,因为这个参数的值会在运算符函数内直接被修改。

在下面的例子中,对 Vector2D 实例实现了一个加法赋值运算符函数:

extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left = left + right
    }
}

因为加法运算在之前已经定义过了,所以在这里无需重新定义。在这里可以直接利用现有的加法运算符函数,用它来对左值和右值进行相加,并再次赋值给左值:

var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
// original 的值现在为 (4.0, 6.0)

不能对默认的赋值运算符(=)进行重载。只有复合赋值运算符可以被重载。同样地,也无法对三元条件运算符 (a ? b : c) 进行重载。

等价运算符

通常情况下,自定义的类和结构体没有对等价运算符进行默认实现,等价运算符通常被称为相等运算符(==)与不等运算符(!=)。

为了使用等价运算符对自定义的类型进行判等运算,需要为“相等”运算符提供自定义实现,实现的方法与其它中缀运算符一样, 并且增加对标准库 Equatable 协议的遵循:

extension Vector2D: Equatable {
    static func == (left: Vector2D, right: Vector2D) -> Bool {
        return (left.x == right.x) && (left.y == right.y)
    }
}

上述代码实现了“相等”运算符(==)来判断两个 Vector2D 实例是否相等。对于 Vector2D 来说,“相等”意味着“两个实例的 x 和 y 都相等”,这也是代码中用来进行判等的逻辑。如果你已经实现了“相等”运算符,通常情况下你并不需要自己再去实现“不等”运算符(!=)。标准库对于“不等”运算符提供了默认的实现,它简单地将“相等”运算符的结果进行取反后返回。

现在我们可以使用这两个运算符来判断两个 Vector2D 实例是否相等:

let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
    print("These two vectors are equivalent.")
}
// 打印“These two vectors are equivalent.”

多数简单情况下,您可以使用 Swift 为您提供的等价运算符默认实现。Swift 为以下数种自定义类型提供等价运算符的默认实现:

  • 只拥有存储属性,并且它们全都遵循 Equatable 协议的结构体
  • 只拥有关联类型,并且它们全都遵循 Equatable 协议的枚举
  • 没有关联类型的枚举

在类型原始的声明中声明遵循 Equatable 来接收这些默认实现。

下面为三维位置向量 (x, y, z) 定义的 Vector3D 结构体,与 Vector2D 类似。由于 xy 和 z 属性都是 Equatable类型,Vector3D 获得了默认的等价运算符实现。

struct Vector3D: Equatable {
    var x = 0.0, y = 0.0, z = 0.0
}

let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
    print("These two vectors are also equivalent.")
}
// 打印“These two vectors are also equivalent.”

发表评论

登录后才能评论

评论列表(0条)