swift4 运算符重载_swift运算符重载 - CSDN
  • Swift 运算符重载

    2016-06-28 11:40:44
    运算符重载 运算符重载允许你改变现在的作用在特定在的结构体和类上的已经存在的操作符的工作方式(译者注:可能有点乱)。这个不正是你想要的吗--改变+操作符作用在int数组上的方式。 因为运算符重载是作用在...

    运算符重载

    运算符重载允许你改变现在的作用在特定在的结构体和类上的已经存在的操作符的工作方式(译者注:可能有点乱)。这个不正是你想要的吗--改变+操作符作用在int数组上的方式。

    因为运算符重载是作用在playground的全局中的,所以新建一个playground,防止影响你原来写的例子。然后添加如下的代码到你的playground:

    1
    2
    3
    4
    5
    6
    7
    8
    func +(left: [Int], right: [Int]) -> [Int] { // 1
        var sum = [Int]() // 2
        assert(left.count == right.count, "vector of same length only")  // 3
        for (key, v) in enumerate(left) {
          sum.append(left[key] + right[key]) // 4
        }
        return sum
    }

    你已经定义了一个全局的函数,叫做+,它将两个int数组相加然后返回一个int数组。下面分解一下它是怎么工作的:

    • 注意这个方法定义没有什么特殊。它是一个普通的方法定义,除了你使用了+作为它的函数名。

    • 你创建了一个空的Int数组。

    • 这个例子只能工作在两个数组是相同的情况上,所以这里使用assert保证它是这样。

    • 然后你枚举了左侧的数组,并且加上了右边的数组在相同位置的值。

    在你的playground添加如下的代码,测试一下这个方法:

    1
    var sumArray1 = [1, 2, 3] + [1, 2, 3]

    最终--你期望的向量相加操作符结果出现了!你将看到如下的结果:

    1
    [2, 4, 6]

    当然,运算符重载并不都是愉快的。当一个人查看你的代码,他们希望操作符的默认行为,这时候运算符重载会使他们迷惑。虽然这样,但是还是不能阻止你重写+运算符让它去执行数字的减法,当然这样的风险是明显的。

    OperatorRage.png

    记住运算符重载的原则:能力越大责任越大

    典型的,当你在一个新的对象上重载运算符的时候,需要保持它原始的语义,而不是定义不同(和让人费解)的行为。

    在这个例子中,重载的行为还是保持了原始的语义:向量加法仍然是一种加法。但是当你覆盖了Int数组默认的加行为的时候,过了几个月你可能想要使用Int数组加得默认行为,这个将会使用感到很困惑。

    幸运的是Swift让你能够定义属于你自己的自定义的运算符。

    定义自定义运算符

    这里有三个步骤去定义一个自定义操作符:

    • 命名你的运算符

    • 选择一种类型

    • 设置它的优先级和结合性

    定义你的运算符

    现在你必须选择一个字符作为你的运算符。自定义运算符可以以/、=、-、+、!、*、%、<、>、&、|、^、~或者Unicode字符开始。这个给了你一个很大的范围去选择你的运算符。但是别太高兴,选择的时候你还必须考虑重复输入的时候更少的键盘键入次数。

    在这种情况下,你可以复制粘贴Unicode字符⊕作为很好适应你例子里面加法的实现。

    选择一种类型

    在Swift中你能定义一元、二元和三元的操作符。他们表明了运算符操作的数字的数目。

    • 一元操作符与一个操作数相关,比如后置++(i++)或者前置++(++i),他们依赖于运算符与操作数出现的位置。

    • 二元操作符是插入的,因为它出现在两个操作符中间,比如1 + 1。

    • 三元操作符有三个操作数。在Swift中,?:条件操作符是唯一一个三目运算符,比如a?b:c。

    你应该基于你的运算符的操作数的个数选择合适得类型。你想要实现两个数组相加,那就定义二元运算符。

    设置它的优先级和结合性

    由于运算符定义是全局的,所以你要小心的选择你的自定义运算符的优先级和结合性。

    这个是十分棘手的,所以有一个比较好的方法,在Swift language reference中找到一个类似的标准的运算符,然后使用相同的语义。例如,在定义向量加的时候,可以使用与+运算符相同的优先级和结合性。

    编写你的自定义运算符

    回到你的playground,输入下面代码去定义你的自定义运算符。为了简单,你可能想去复制粘贴⊕。(译者注:这里可能指的是在使用的过程中去复制这个字符)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    infix operator ⊕ { associativity left precedence 140 } // 1
    func ⊕(left: [Int], right: [Int]) -> [Int] { // 2
        var sum = [Int](count: left.count, repeatedValue: 0)
        assert(left.count == right.count, "vector of same length only")
        for (key, v) in enumerate(left) {
            sum[key] = left[key] + right[key]
        }
        return sum
    }

    这段代码与你前面在第一部分中的重载类似,这段代码主要做了以下几个步骤:

    • 定义一个中缀/二元操作符,它有两个操作数并且位于操作符两侧。

    • 命名操作符为⊕。

    • 设置结合性为left,表明该操作符在相同优先级时候,将使用操作符的顺序从左到右结合。

    • 设置优先级为140,这个是和Int加法有相同的优先级,这些优先级可以在Swift language reference查看。

    在第二部分的代码和你在前面看到的类似,它按照两个数组的顺序将其一个一个的相加。在你的playground中添加下面的代码,测试这个新的运算符:

    1
    var sumArray = [1, 2, 3] ⊕ [1, 2, 3]

    你将看到和前面重载方法一样的结果,但是这次你有了一个拥有不同语义的操作符。

    奖励内容

    现在你已经知道了怎么去创建一个自定义的运算符,是时候挑战一下你自己了。你已经创建了一个⊕运算符去执行向量的相加,所以使用现在的知识去创建一个?操作符,使用相似的方法实现两个数组的减法。尽你最大的努力,然后再去查看下面的答案。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    infix operator  ? { associativity left precedence 140 }
    func ?(left: [Int], right: [Int]) -> [Int] {
      var minus = [Int](count: left.count, repeatedValue: 0)
      assert(left.count == right.count, "vector of same length only")
      for (key, v) in enumerate(left) {
        minus[key] = left[key] - right[key]
      }
      return minus
    }

    测试:

    1
    var subtractionArray = [1, 2, 3] ? [1, 2, 3]

    记住相似的操作符

    如果你定义了一个新的操作符,不要忘了定义任何相关得运算符。

    例如,加等运算符(+=)组合了加和赋值两个运算符成为了一个运算符。由于你的新的运算符语义上是跟加是一样的,一个好的方法是也定义一个加等于运算符。

    添加下面得代码到Operator2.playground:

    1
    2
    3
    4
    infix operator  ⊕= { associativity left precedence 140 } // 1
    func ⊕=(inout left: [Int], right: [Int]) { // 2
        left = left ⊕ right
    }

    第一行是与⊕运算符一样得声明,它使用了一个组合运算符。

    需要注意第二行,声明这个组合运算符的左侧输入参数为inout,这个表示这个参数的值,将会在运算符方法内部直接被修改。作为一个结果,这个运算符不用返回一个值,它直接修改了你的输入值。

    在你的playground添加如下的代码,测试这个运算符是否按照你想的方法运行。

    你将在控制台看到如下输出:

    1
    [3, 5, 7]

    现在看看,定义你自己的运算符一点也不难。

    为更多类型定义运算符

    现在想象你也想为小数定义一个向量加运算符。

    一种方式是你按照为Int重载运算符的方式,为Double和Float重载一个新的运算符。它仅仅是几行的代码,但是你必须使用复制/粘贴。如果你像我一样--有代码洁癖--复制代码不是你的第一选择,它会使你的代码很难维护。

    使用泛型来解救

    幸运的,Swift泛型能帮助你实现这个功能。如果你需要复习一下Swift的泛型,可以找到我们之前发布的文章Swift Generics Tutorial

    为了有一个干净的上下文环境,我们新建一个playground。添加如下的代码到你的playground中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    infix operator ⊕ { associativity left precedence 140 }
    func ⊕(left: [T], right: [T]) -> [T] { // 1
        var minus = [T]()
        assert(left.count == right.count, "vector of same length only")
        for (key, v) in enumerate(left) {
            minus.append(left[key] + right[key]) // 2
        }
        return minus
    }

    在第一行,你定义了一个泛型类型得函数⊕,它有一个类型占位符T。到这里playground不高兴了。你能看到一个编译错误:Could not find an overload for '+' that accepts the supplied arguments.

    这个错误来源于第二行,当我们尝试使用+运算符作用在两个类型为T得left和right两个参数上的时候发生错误。Swift并不知道它应该怎么使用+运算符作用在这些参数上,因为它不知道这些参数是什么类型。

    扩展一个协议

    去掉你的代码,并且用下面的代码代替:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    protocol Number {  // 1
        func +(l: Self, r: Self) -> Self // 2
    }
      
    extension Double : Number {} // 3
    extension Float  : Number {}
    extension Int    : Number {}
      
    infix operator ⊕ { associativity left precedence 140 }
    func ⊕(left: [T], right: [T]) -> [T] { // 4
        var minus = [T]()
        assert(left.count == right.count, "vector of same length only")
        for (key, v) in enumerate(left) {
            minus.append(left[key] + right[key])
        }
        return minus
    }

    你在这里做了许多的事情,我们回过头来分解一下这些步骤:

    • 你定义了一个协议Number

    • 这个Number定义了一个运算符+

    • 你为Double、Float和Int创建了一个扩展,使它们能够实现Number协议

    • 你使用了一个类型约束去要求T必须实现Number协议

    最后,你告诉编译器,T应该怎么去处理+运算符。既然你已经修复了编译错误,那就使用下面得代码分别使用Double数组和Int数组测试一下吧:

    1
    2
    var doubleArray = [2.4, 3.6] ⊕ [1.6, 2.4]
    var intArray = [2, 4] ⊕ [1, 2]

    你将在控制台看到如下输出:

    1
    2
    [4.0, 6.0]
    [3, 6]

    现在这个运算符能够正常在多种数据类型下面工作,并且没有复制代码。如果你想添加更多得数字类型,你只需要简单的生命其实现Number协议就可以了。

    实际工作中如何使用运算符重载

    难道你就没有想过,如果它没有作用,我会让你浪费这么多的时间在这篇教程上吗?这一部分将要展示给你一个真实得例子,让你了解怎么样在你的项目中更好得使用运算符重载。

    运算符和CGPoints

    对于这个Demo,你将使用SKTUtils library,它是一个方便得Sprite Kit帮助类的集合,当时是为了iOS Games by Tutorials这本书的第二版而写的。

    你能在github上找到这个框架的仓库。在你命令行界面输入如下的代码,可以Clone一份这个仓库的分支:

    git clone https://github.com/raywenderlich/SKTUtils.git --branch swift

    你在github上下载下来的是该仓库分支的压缩包zip。

    注意:从Xcode6 beta 5开始,在playground中引入你自己的library成为了可能。你需要做的就是将框架和playground绑定在一个workspace中。如果你想知道更多关于这些的内容,请阅读这篇文章Playground has never been so fun

    打开SKUTils/Examples/Playground/SKUTils.xcodeworkspace,并且编译这个项目。

    然后从项目导航里面打开MyPlayground.playground。删除现在里面的内容并且添加如下的代码:

    1
    2
    3
    4
    5
    import SKTUtils 
    let pt1 = CGPoint(x: 10, y: 20)
    let pt2 = CGPoint(x: -5, y: 0)
    let pt3 = pt1 + pt2 
    let pt4 = pt3 * 100

    你可能很惊讶,你已经在CGPoint上成功的使用+、*运算符,并且编译器并没有出现错误。

    1
    2
    3
    4
    {x 10 y 20}
    {x -5 y 0}
    {x 5 y 20}
    {x 500 y 2,000}

    这个魔法来自于你在头部引入的SKTUtils。让我们仔细的看一下。

    在SKTUtils中的重载

    在项目导航中打开SKTUtils/CGPoint+Extension.swift文件。你将看到为CGPoint定义了一个扩展,重载了+和+=运算符。

    1
    2
    3
    4
    5
    6
    7
    public func + (left: CGPoint, right: CGPoint) -> CGPoint {
      return CGPoint(x: left.x + right.x, y: left.y + right.y)
    }
      
    public func += (inout left: CGPoint, right: CGPoint) {
      left = left + right
    }

    这段代码跟你前面写的类似,只是把访问控制符设置成了public。访问控制符约束着在其他得源文件和模块中能否访问到你的代码。由于SKTUtils是一个框架,所以它需要能够被它自己模块之外访问到,所以定义为了public。

    这个戏法解释清楚了,它没有一点魔力,只是一个聪明得编码。

    当然,在游戏中CGPoint加法和乘法也是一个很普通得运算,所以在那本书中,重载了CGPoint的运算符简化了代码,使它简洁、易读。我相信你能在你的项目中发现类似的例子。

    运算符重载是Swift的一个强大得特性,如果你小心的使用它,它会使你的开发更加高效。

    展开全文
  • 1,通过重载加号运算符,使自定义的两个坐标结构体对象实现相加: struct CenterPointer{ var x=0, y=0 } func + (left:CenterPointer, right:CenterPointer) -> CenterPointer{ return CenterPointer(x:left.x...

    1,通过重载加号运算符,使自定义的两个坐标结构体对象实现相加:

    struct CenterPointer{
        var x=0, y=0
    }
     
    func + (left:CenterPointer, right:CenterPointer) -> CenterPointer{
        return CenterPointer(x:left.x+right.y, y:left.y+right.y)
    }
     
    let pointer1 = CenterPointer(x:2, y:3)
    let pointer2 = CenterPointer(x:4, y:5)
    let pointer3 = pointer1 + pointer2

    2,重载判断运算符,实现判断自定义类型是否相等

    func == (left:CenterPointer, right:CenterPointer) -> Bool {
        return (left.x == right.x) && (left.y == right.y)
    }
     
    func != (left:CenterPointer, right:CenterPointer) -> Bool {
        return !(left == right)
    }

    3,组合运算符,即将其他运算符和赋值运算符组合在一起,注意要把运算符左参数设置成inout类型

    func += (inout left:CenterPointer, right:CenterPointer){
        left = left + right
    }
     
    var pointer1 = CenterPointer(x:2, y:3)
    var pointer2 = CenterPointer(x:4, y:5)
    pointer1 += pointer2


    展开全文
  • Swift 运算符重载简介

    2019-04-20 08:14:36
    在任何一门计算机编程语言中,运算符重载都是非常强大的特性之一,因此苹果决定为 Swift 也提供这一机制。然而,"能力越强责任越大"。利用运算符重载你很容易实现一些奇怪的场景,例如用减法运算符实现两数相加,...
        

    作者:COSMIN PUPĂZĂ,原文链接,原文日期:2016-03-29
    译者:zltunes;校对:shanks;定稿:小锅

    在任何一门计算机编程语言中,运算符重载都是非常强大的特性之一,因此苹果决定为 Swift 也提供这一机制。然而,"能力越强责任越大"。利用运算符重载你很容易实现一些奇怪的场景,例如用减法运算符实现两数相加,或者用乘法运算符实现两数相除,但这显然都不是你希望出现的。

    好了,闲话少叙 —— 让我们看看运算符重载究竟是怎么一回事。

    挑战

    这一小节的任务很简单:扩展乘法运算符的标准功能,使其适用于字符串。你将会用到字符串拼接运算符,想象一下这种用法:

    "abc" * 5 = "abc" + "abc" + "abc" + "abc" + "abc" = "abcabcabcabcabc"
    

    正式编码之前,思考一下应该怎么做,分几步来实现。我的做法是这样的:

    • 定义变量 result 并进行初始化 —— 默认字符串。

    • 从 2 开始循环,一直到待拼接的字符串数目终止,每次迭代只做一件事 —— 把字符串拼接到 result 末尾。

    • 打印 result

    算法大致就是这样,接下来让我们付诸实践。

    基本运算符重载


    启动 Xcode 并新建一个 playground 文件。删除原有代码,添加乘法运算符的函数原型:

    func *(lhs: String, rhs: Int) -> String {
     
    }
    

    函数有两个参数 —— 左操作数是 String 类型,右操作数是 Int 类型,函数返回类型为 String
    函数体内应该完成三件事。首先,定义 result 变量并初始化为函数的 String 参数 —— 这是一个变量,稍后会修改它的值。

    var result = lhs
    

    接下来使用 for in 控制流语句及闭区间运行符从 2 开始循环,直到函数的 Int 参数时为止:

    for _ in 2...rhs {
     
    }
    

    注意:这里使用了 _ 作为通配符,因为我们希望忽略序列的具体值 —— 关于循环的更多说明可以看这里

    循环体内只有一个任务 —— 更新 result

    result += lhs
    

    注意:你也可以按如下方式来写 —— 上边这种写法更短,是因为用了加法复合运算符。

    result = result + lhs
    

    最后返回 result:

    return result
    

    现在我们直接使用运算符:

    let u = "abc"
    let v = u * 5
    

    搞定了!只是还有一个问题 —— 你只能将其用于字符串,那其它类型的数据怎么办?我们使用范型运算符来完善。

    泛型运算符


    泛型默认是不支持运算符的,所以需要协议来支持。向 playground 中添加协议原型:

    protocol Type {
     
    }
    

    现在向协议中添加加法复合运算符函数的原型:

    func +=(inout lhs: Self, rhs: Self)
    

    函数拥有左右操作数,并且都设置为 Self 类型 —— 这是一种巧妙的方式,说明二者的类型都是实现了该协议的类。左操作数标记为inout,因为它的值是要被修改并且最后被函数返回的。

    或者,你也可以定义加法运算符的函数原型:

    func +(lhs: Self, rhs: Self) -> Self
    

    函数拥有 Self 类型的左右操作数,并且加法运算的返回结果也是 Self 。这种情况下就不需要使用 inout 参数了。

    接下来,为 String , Int , Double , Float 等实现了 Type 协议的类型创建扩展。

    extension String: Type {}
    extension Int: Type {}
    extension Double: Type {}
    extension Float: Type {}
    

    注意:这些扩展的实现是空的,因为你并不打算为默认类型添加任何东西,仅仅是要让他们遵循 Type 协议。

    现在向 playground 中添加乘法操作符函数原型:

    func *<T: Type>(lhs: T, rhs: Int) -> T {
     
    }
    

    函数有两个参数,左操作数是 T 类型,右操作数是 Int 类型,函数返回类型为 T 。利用类型约束使 T 类型遵循 Type 协议,这样它就可以使用加法复合运算符了。

    注意:你可以使用 where 关键字定义类型约束——尽管上边的方法更简短:

    func *<T where T: Type>(lhs: T, rhs: Int) -> T
    

    函数的实现跟之前一样:

    var result = lhs
     
    for _ in 2...rhs {
     
        result += lhs
        
    }
     
    return result
    

    注意:可以使用加法操作符替代,但要确保它的函数原型添加到了协议中。
    测试一下:

    let x = "abc"
    let y = x * 5
     
    let a = 2
    let b = a * 5
     
    let c = 3.14
    let d = c * 5
     
    let e: Float = 4.56
    let f = e * 5
    

    搞定了!不过有一个问题:你使用的是标准乘法运算符,这个可能造成歧义。如果换成其它运算符会更好。接下来我们试着用自定义运算符解决这个问题。

    自定义运算符

    首先添加下面一行到 playground:

    infix operator ** {associativity left precedence 150}
    

    一步一步解释:

    • 自定义乘法运算符的名称是 **。

    • 类型是 中缀运算符(infix) 因为它有两个操作数。

    • 运算顺序从左至右,因此是左结合。

    • 优先级设置为 150 —— 与标准乘法运算符相同,因为它是高优先级运算符。

    注意:关于运算符优先级和结合性的更多说明可以看这里

    自定义运算符的函数原型与标准运算符类似 —— 只有函数名不同:

    func **<T: Type>(lhs: T, rhs: Int) -> T {
     
    }
    

    函数实现跟之前完全一样:

    var result = lhs
     
    for _ in 2...rhs {
     
        result += lhs
        
    }
     
    return result
    

    测试一下:

    let g = "abc"
    let h = g ** 5
     
    let i = 2
    let j = i ** 5
     
    let k = 3.14
    let l = k ** 5
     
    let m: Float = 4.56
    let n = m ** 5
    

    搞定了!还有一个问题——运算符的复合类型还没有定义,接下来我们解决这个问题:

    复合运算符


    复合运算符的类型、优先级和结合性和之前一样 —— 只有名称不同:

    infix operator **= {associativity left precedence 150}
    

    接着向 playground 添加复合运算符的函数原型:

    func **=<T: Type>(inout lhs: T, rhs: Int) {
     
    }
    

    函数没有返回类型,因为左操作数被标记为 inout
    函数体只做一件事 —— 运用之前的自定义运算符返回乘法结果:

    lhs = lhs ** rhs
    

    测试一下:

    var o = "abc"
    o **= 5
     
    var q = 2
    q **= 5
     
    var s = 3.14
    s **= 5
     
    var w: Float = 4.56
    w **= 5
    

    搞定了!这已经是最简版本了!

    总结

    如果谨慎地使用运算符重载,它便能够发挥强大的功能 —— 我希望你能在自己的项目中找到合适使用的方法。
    作为参考,这里有一个完整版的 Playground,我已经在 Xcode 7.3 用 Swift 2.2 测试过了。

    如果你对本教程或者运算符重载有什么看法的话可以给我留言。

    致谢:连环画是在 MakeBeliefsComix.com 制作的。
    本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问 http://swift.gg

    展开全文
  • Swift-运算符重载

    2016-10-25 15:56:45
    原文地址:http://www.raywenderlich.com/80818/operator-overloading-in-swift-tutorial 作者:Corinne Krych 译者:孟祥月 blog:http://blog.csdn.net/mengxiangyue 这篇文章是本人第一次翻译,难免有错误,...

    原文地址:http://www.raywenderlich.com/80818/operator-overloading-in-swift-tutorial 作者:Corinne Krych  译者:孟祥月 blog:http://blog.csdn.net/mengxiangyue

    这篇文章是本人第一次翻译,难免有错误,翻译的时候使用的是txt,所以格式上面有些不太好。


    在早前的IOS 8盛宴系列的教程里,你已经了解到,Swift提供了许多强大的、现代的编程特性,比如泛型、函数式编程、一等类型(first class)的枚举、结构体等特性。

    但是现在还有另外一个Swift的特性,你应该知道并且会爱上它,它就是运算符重载。

    这是一个很好的方法,你能使用+、-、*、/等操作符作用在你喜欢的任何类型上面。如果你有一定的创造性,你甚至可以定义属于你自己的操作符。

    例如:我们在Swift Sprite Kit utility library(https://github.com/raywenderlich/SKTUtils/tree/swift)代码中使用运算符重载去讲多个CGPoints对象相加,例如下面代码:
    let pt1 = CGPoint(x: 10, y: 20)
    let pt2 = CGPoint(x: -5, y: 0)
    let pt3 = pt1 + pt2
    let pt4 = pt3 * 100

    方便吧?那就马上开始重载吧,增强你的Swift开发的能力吧。


    注意:这个Swift的教程是假设你已经具备了基础的Swift开发能力。如果你是新接触Swift,我们建议你先去学习我们的其他的Swift教程(http://www.raywenderlich.com/tutorials#swift).


    运算符:概述
    注意:这一部分的内容是可选的,如果你想回顾一下运算符及其优先级,还是可以看这部分内容的。如果你已经对这些很熟悉了,可以直接创建一个空的playground,进行下一部分内容:重载(Overloading)。


    首先我们创建一个新的playground来帮助你去了解运算符。
    添加如下的代码在你的playground中:
    var simpleSum = 1 + 3
    你能看到我们希望的结果:
    4
    这里有两个我们熟悉的操作符:
    1 首先,你定义了一个叫做simpleSum的变量,并且使用赋值操作符(=)设置了它的值。
    2 然后,你使用加操作符(+)计算了两个整数的和。


    在这篇教程里,你将像这样重载操作符。但是首先,你需要理解优先级的概念。
    优先级
    你可能还记得在学校里的数学课上学过的关于操作符的优先级的规则。这些规则使某些操作符比其他得操作符有一个更高的优先级,高优先级的操作符被优先计算。例如乘会在加或者减之前计算。
    在你的playground中输入以下的代码,验证在Swift中是否也遵循这些规则。
    var sumWithMultiplication = 1 + 3 - 3 * 2
    你能看到如下的结果:
    -2
    当算数操作符有相同的优先级的时候,Swift从左到右去计算这些操作符。在这个例子中,运算符按照如下的顺序计算的:
    1.3 * 2:减去(译者注:这个减去可以忽略,主要是为了对应第三步)
    2.1 + 3:因为在操作符优先级一样得情况下,优先计算最左边得操作符。
    3.4 - 6:这个运算完全依赖于前面高优先级的运算符的运算结果。


    注意:如果你想了解Swift中优先级的列表,你能在这里(https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-XID_720)找到完成的运算符优先级的列表。


    加不是仅仅能够使用在数字上(Adding Isn’t Just for Ints)
    整数运算会按照我们希望的运行,但是你能将+使用到其他的类型上吗?
    下面代码证明了,你可以!在你的playground里面添加如下的代码试一试:
    var sumArray = [1, 2] + [1, 2]
    在这种情况下,Swift将+解释成为append指令。但是如果你是想把每一个位置的元素相加怎么办呢?我们都知道这个叫做向量加法(vector addition)。
    当然,你能自己定义一个方法去实现这个功能,在你的playground添加如下的代码再试一试:
    func add(left: [Int], right: [Int]) -> [Int] {
        var sum = [Int]() 
        assert(left.count == right.count, "vector of same length only") 
        for (key, v) in enumerate(left) {
            sum.append(left[key] + right[key]) 
        }
        return sum
    }
    这样你就定义了一个全局的方法,这个方法实现了计算输入的两个数组的相加,首先检测两个输入的数组的长度是否一致,然后将两个数组每一个位置上的元素相加并且存储到一个新的数组里面。
    现在添加下面的代码,验证一下你的新方法是否工作正常:
    var arr1 = [1, 1]
    var arr2 = [1, 1]
    var arr3 = add(arr1, arr2)
    你将在控制台看到如下的输出:
    [2, 2]
    它很棒!但是我们必须去调用一个方法去做这件事,为什么我们不可以使用+运算符代替呢?

    运算符重载
    运算符重载允许你改变现在的作用在特定在的结构体和类上的已经存在的操作符的工作方式(译者注:可能有点乱)。这个不正是你想要的吗--改变+操作符作用在int数组上的方式。
    因为运算符重载是作用在playground的全局中的,所以新建一个playground,防止影响你原来写的例子。然后添加如下的代码到你的playground:
    func +(left: [Int], right: [Int]) -> [Int] { // 1
        var sum = [Int]() // 2
        assert(left.count == right.count, "vector of same length only")  // 3
        for (key, v) in enumerate(left) {
          sum.append(left[key] + right[key]) // 4
        }
        return sum
    }
    你已经定义了一个全局的函数,叫做+,它将两个int数组相加然后返回一个int数组。下面分解一下它是怎么工作的:
    1.注意这个方法定义没有什么特殊。它是一个普通的方法定义,除了你使用了+作为它的函数名。
    2.你创建了一个空的Int数组。
    3.这个例子只能工作在两个数组是相同的情况上,所以这里使用assert保证它是这样。
    4.然后你枚举了左侧的数组,并且加上了右边的数组在相同位置的值。
    在你的playground添加如下的代码,测试一下这个方法:
    var sumArray1 = [1, 2, 3] + [1, 2, 3]
    最终--你期望的向量相加操作符结果出现了!你将看到如下的结果:
    [2, 4, 6]
    当然,运算符重载并不都是愉快的。当一个人查看你的代码,他们希望操作符的默认行为,这时候运算符重载会使他们迷惑。虽然这样,但是还是不能阻止你重写+运算符让它去执行数字的减法,当然这样的风险是明显的。
    image http://cdn4.raywenderlich.com/wp-content/uploads/2014/09/OperatorRage.png
    记住运算符重载的原则:能力越大责任越大(with great power comes great responsibility)。
    典型的,当你在一个新的对象上重载运算符的时候,需要保持它原始的语义,而不是定义不同(和让人费解)的行为。
    在这个例子中,重载的行为还是保持了原始的语义:向量加法仍然是一种加法。但是当你覆盖了Int数组默认的加行为的时候,过了几个月你可能想要使用Int数组加得默认行为,这个将会使用感到很困惑。
    幸运的是Swift让你能够定义属于你自己的自定义的运算符。
    定义自定义运算符
    这里有三个步骤去定义一个自定义操作符:
    1.命名你的运算符
    2.选择一种类型
    3.设置它的优先级和结合性
    定义你的运算符
    现在你必须选择一个字符作为你的运算符。自定义运算符可以以/、=、-、+、!、*、%、<、>、&、|、^、~或者Unicode字符开始。这个给了你一个很大的范围去选择你的运算符。但是别太高兴,选择的时候你还必须考虑重复输入的时候更少的键盘键入次数。
    在这种情况下,你可以复制粘贴Unicode字符⊕作为很好适应你例子里面加法的实现。
    选择一种类型
    在Swift中你能定义一元、二元和三元的操作符。他们表明了运算符操作的数字的数目。
    一元操作符与一个操作数相关,比如后置++(i++)或者前置++(++i),他们依赖于运算符与操作数出现的位置。
    二元操作符是插入的,因为它出现在两个操作符中间,比如1 + 1。
    三元操作符有三个操作数。在Swift中,?:条件操作符是唯一一个三目运算符,比如a?b:c。
    你应该基于你的运算符的操作数的个数选择合适得类型。你想要实现两个数组相加,那就定义二元运算符。
    设置它的优先级和结合性
    由于运算符定义是全局的,所以你要小心的选择你的自定义运算符的优先级和结合性。
    这个是十分棘手的,所以有一个比较好的方法,在Swift language reference(https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-XID_720)中找到一个类似的标准的运算符,然后使用相同的语义。例如,在定义向量加的时候,可以使用与+运算符相同的优先级和结合性。
    编写你的自定义运算符
    回到你的playground,输入下面代码去定义你的自定义运算符。为了简单,你可能想去复制粘贴⊕。(译者注:这里可能指的是在使用的过程中去复制这个字符)
    infix operator ⊕ { associativity left precedence 140 } // 1
    func ⊕(left: [Int], right: [Int]) -> [Int] { // 2
        var sum = [Int](count: left.count, repeatedValue: 0)
        assert(left.count == right.count, "vector of same length only")
        for (key, v) in enumerate(left) {
            sum[key] = left[key] + right[key]
        }
        return sum
    }
    这段代码与你前面在第一部分中的重载类似,这段代码主要做了以下几个步骤:
    * 定义一个中缀/二元操作符,它有两个操作数并且位于操作符两侧。
    * 命名操作符为⊕。
    * 设置结合性为left,表明该操作符在相同优先级时候,将使用操作符的顺序从左到右结合。
    * 设置优先级为140,这个是和Int加法有相同的优先级,这些优先级可以在Swift language reference(https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-XID_720)查看。
    在第二部分的代码和你在前面看到的类似,它按照两个数组的顺序将其一个一个的相加。在你的playground中添加下面的代码,测试这个新的运算符:
    var sumArray = [1, 2, 3] ⊕ [1, 2, 3]
    你将看到和前面重载方法一样的结果,但是这次你有了一个拥有不同语义的操作符。
    Bonus Round!
    现在你已经知道了怎么去创建一个自定义的运算符,是时候挑战一下你自己了。你已经创建了一个⊕运算符去执行向量的相加,所以使用现在的知识去创建一个⊖操作符,使用相似的方法实现两个数组的减法。尽你最大的努力,然后再去查看下面的答案。
    infix operator  ⊖ { associativity left precedence 140 }
    func ⊖(left: [Int], right: [Int]) -> [Int] {
      var minus = [Int](count: left.count, repeatedValue: 0)
      assert(left.count == right.count, "vector of same length only")
      for (key, v) in enumerate(left) {
        minus[key] = left[key] - right[key]
      }
      return minus
    }
    测试:
    var subtractionArray = [1, 2, 3] ⊖ [1, 2, 3]


    记住相似的操作符
    如果你定义了一个新的操作符,不要忘了定义任何相关得运算符。
    例如,加等运算符(+=)组合了加和赋值两个运算符成为了一个运算符。由于你的新的运算符语义上是跟加是一样的,一个好的方法是也定义一个加等于运算符。
    添加下面得代码到Operator2.playground:
    infix operator  ⊕= { associativity left precedence 140 } // 1
    func ⊕=(inout left: [Int], right: [Int]) { // 2
        left = left ⊕ right
    }
    第一行是与⊕运算符一样得声明,它使用了一个组合运算符。
    需要注意第二行,声明这个组合运算符的左侧输入参数为inout,这个表示这个参数的值,将会在运算符方法内部直接被修改。作为一个结果,这个运算符不用返回一个值,它直接修改了你的输入值。
    在你的playground添加如下的代码,测试这个运算符是否按照你想的方法运行。
    你将在控制台看到如下输出:
    [3, 5, 7]
    现在看看,定义你自己的运算符一点也不难。


    为不仅仅是一种类型定义运算符
    现在想象你也想为小数定义一个向量加运算符。
    一种方式是你按照为Int重载运算符的方式,为Double和Float重载一个新的运算符。它仅仅是几行的代码,但是你必须使用复制/粘贴。如果你像我一样--有代码洁癖--复制代码不是你的第一选择,它会使你的代码很难维护。
    使用泛型来解救
    幸运的,Swift泛型能帮助你实现这个功能。如果你需要复习一下Swift的泛型,可以找到我们之前发布的文章Swift Generics Tutorial(http://www.raywenderlich.com/82572/swift-generics-tutorial)。
    为了有一个干净的上下文环境,我们新建一个playground。添加如下的代码到你的playground中:
    infix operator ⊕ { associativity left precedence 140 }
    func ⊕<T>(left: [T], right: [T]) -> [T] { // 1
        var minus = [T]()
        assert(left.count == right.count, "vector of same length only")
        for (key, v) in enumerate(left) {
            minus.append(left[key] + right[key]) // 2
        }
        return minus
    }
    在第一行,你定义了一个泛型类型得函数⊕,它有一个类型占位符T。到这里playground不高兴了。你能看到一个编译错误:Could not find an overload for '+' that accepts the supplied arguments.
    这个错误来源于第二行,当我们尝试使用+运算符作用在两个类型为T得left和right两个参数上的时候发生错误。Swift并不知道它应该怎么使用+运算符作用在这些参数上,因为它不知道这些参数是什么类型。
    扩展一个协议
    去掉你的代码,并且用下面的代码代替:
    protocol Number {  // 1
         func +(l: Self, r: Self) -> Self // 2
    }
     
    extension Double : Number {} // 3
    extension Float  : Number {}
    extension Int    : Number {}
     
    infix operator ⊕ { associativity left precedence 140 }
    func ⊕<T: Number>(left: [T], right: [T]) -> [T] { // 4
        var minus = [T]()
        assert(left.count == right.count, "vector of same length only")
        for (key, v) in enumerate(left) {
            minus.append(left[key] + right[key])
        }
        return minus
    }
    你在这里做了许多的事情,我们回过头来分解一下这些步骤:
    1. 你定义了一个协议Number
    2. 这个Number定义了一个运算符+
    3. 你为Double、Float和Int创建了一个扩展,使它们能够实现Number协议
    4. 你使用了一个类型约束去要求T必须实现Number协议
    最后,你告诉编译器,T应该怎么去处理+运算符。既然你已经修复了编译错误,那就使用下面得代码分别使用Double数组和Int数组测试一下吧:
    var doubleArray = [2.4, 3.6] ⊕ [1.6, 2.4]
    var intArray = [2, 4] ⊕ [1, 2]
    你将在控制台看到如下输出:
    [4.0, 6.0]
    [3, 6]
    现在这个运算符能够正常在多种数据类型下面工作,并且没有复制代码。如果你想添加更多得数字类型,你只需要简单的生命其实现Number协议就可以了。


    在真实得生活中我还能怎么使用运算符重载
    难道你就没有想过,如果它没有作用,我会让你浪费这么多的时间在这篇教程上吗?这一部分将要展示给你一个真实得例子,让你了解怎么样在你的项目中更好得使用运算符重载。
    运算符和CGPoints
    对于这个Demo,你将使用SKTUtils library(https://github.com/raywenderlich/SKTUtils/tree/swift),它是一个方便得Sprite Kit帮助类的集合,当时是为了 iOS Games by Tutorials(http://www.raywenderlich.com/store/ios-games-by-tutorials)这本书的第二版而写的。
    你能在github上找到这个框架的仓库。在你命令行界面输入如下的代码,可以Clone一份这个仓库的分支:
    git clone https://github.com/raywenderlich/SKTUtils.git --branch swift
    你在github上下载下来的是该仓库分支的压缩包zip。
    注意:从Xcode6 beta 5开始,在playground中引入你自己的library成为了可能。你需要做的就是将框架和playground绑定在一个workspace中。如果你想知道更多关于这些的内容,请阅读这篇文章Playground has never been so fun(http://corinnekrych.blogspot.fr/2014/08/playground-has-never-been-so-fun.html)。
    打开SKUTils/Examples/Playground/SKUTils.xcodeworkspace,并且编译这个项目。
    然后从项目导航里面打开MyPlayground.playground。删除现在里面的内容并且添加如下的代码:
    import SKTUtils 
     
    let pt1 = CGPoint(x: 10, y: 20)
    let pt2 = CGPoint(x: -5, y: 0)
    let pt3 = pt1 + pt2 
    let pt4 = pt3 * 100
    你可能很惊讶,你已经在CGPoint上成功的使用+、*运算符,并且编译器并没有出现错误。
    {x 10 y 20}
    {x -5 y 0}
    {x 5 y 20}
    {x 500 y 2,000}
    这个魔法来自于你在头部引入的SKTUtils。让我们仔细的看一下。


    在SKTUtils中的重载
    在项目导航中打开SKTUtils/CGPoint+Extension.swift文件。你将看到为CGPoint定义了一个扩展,重载了+和+=运算符。
    public func + (left: CGPoint, right: CGPoint) -> CGPoint {
      return CGPoint(x: left.x + right.x, y: left.y + right.y)
    }
     
    public func += (inout left: CGPoint, right: CGPoint) {
      left = left + right
    }
    这段代码跟你前面写的类似,只是把访问控制符设置成了public。访问控制符约束着在其他得源文件和模块中能否访问到你的代码。由于SKTUtils是一个框架,所以它需要能够被它自己模块之外访问到,所以定义为了public。
    这个戏法解释清楚了,它没有一点魔力,只是一个聪明得编码。
    当然,在游戏中CGPoint加法和乘法也是一个很普通得运算,所以在那本书中,重载了CGPoint的运算符简化了代码,使它简洁、易读。我相信你能在你的项目中发现类似的例子。
    运算符重载是Swift的一个强大得特性,如果你小心的使用它,它会使你的开发更加高效。

    展开全文
  • swift运算符重载

    2017-10-24 20:03:36
    class Complex { var real = 0 var virtual = 0 init(real: Int,virtual: Int) { self.real = real; self.virtual = virtual } func show() -> Void { if real != 0 {
  • 1. 普通运算符和比较运算符 import Foundation struct Vector3 { var x: Double var y: Double var z: Double } var va = Vector3(x: 1, y: 2, z: 3) var vb = Vector3(x: 4, y: 5, z: 6) func + (left: ...
  • Swift: 运算符重载

    2019-06-11 23:00:13
    Swift中提供了一个新的特性:运算符重载运算符重载允许我们将已经存在的运算符用于其他结构类型的对象。比方说‘+’号通过重载,可以用于两个矢量的相加,而不仅仅只用于Int或者Double等类型的运算。 比方说: ...
  • 像这样定义一个结构体 struct Vector2D {  var x = 0.0  var y = 0.0 } 当 Vector2D 的两个变量相加时,我们需要这样做: let v1 = Vector2D(x: 2.0, y: 3.0) let v2 = Vector2D(x: 4.0, y: 5.0) ...
  • Swift运算符重载的控制很类似 Haskell,而不是 C 系列。如下: 支持自定义运算符 / = - + * % ! & | ^ . ~ 的任意组合。可以脑洞大开创造颜文字。支持前缀(例如默认的 ~,!,++, -- ),后缀(后置的++, --...
  • 1. Swift的运算符提供了良好的可扩展性,Swift不仅允许开发者重新定义已有的运算符(运算符重载),甚至允许开发者实现新的运算符。从本质上来看,Swift的运算符其实就是一个运算符函数,因此只要能用函数实现的功能...
  • Swift 高级运算符

    2014-06-12 23:31:13
    除了基本操作符中所讲的运算符Swift还有许多复杂的高级运算符,包括了C语和Objective-C中的位运算符和移位运算。   不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的。溢出行为会被捕获并报告为...
  • Swift运算符重载

    2014-10-03 18:34:29
    //向量struct Vector2D { var x = 0.0 var y = 0.0}//重载加号func + (left: Vector2D, right: Vector2D) -> Vector2D { return Vector2D(x: left.x + right.x, y: left.y + right.y) }let vector = Vector2D(x: 3.0...
  • swift:高级运算符 http://www.cocoachina.com/ios/20140612/8794.html 除了基本操作符中所讲的运算符Swift还有许多复杂的高级运算符,包括了C语和Objective-C中的位运算符和移位运算。 不同于C语言中的...
  • 溢出运算符 默认情况下,当你往一个整型常量或变量赋于一个它不能承载的大数时,Swift不会让你这么干的,它会报错。这样,在操作过大或过小的数的时候就很安全了。 例如,Int16整型能承载的整数范围是-32768到...
  • 高级运算符 1.位运算符 1.1按位取反 前置运算符 ~ 对一个操作数的每一位都取反,后紧跟操作数,不加空格 let initialBits: UInt8 = 0b00001111 let invertedBits = ~initialBits // equals 11110000 ...
1 2 3 4 5 ... 20
收藏数 1,110
精华内容 444
热门标签
关键字:

swift4 运算符重载