swift 之运算符

基本概念

  • 一元运算符:对一个目标进行操作,如一元前缀运算符(!flag)和用于解包的一元后缀运算符(b!)。
  • 二元运算符(中缀)。
  • 三元运算符: flag ? a : b

赋值和算术运算符

swift支持c中大多数标准运算符,并且:

  • 赋值运算符(=)不会返回值,防止误用为等于(==)。
  • 算术运算符(+、-、*、/、 %)可以检测并阻止值溢出,甚至提供了可以允许值溢出的算术运算符。
  • 加法运算符可以用于字符串拼接。
  • 一元算术运算符:切换正负值,比如(+5,-5)。
  • 取余运算符需要注意:被除数正负号不会被忽略,除数正负号会被忽略。

print(3 % 2) // 1
print(-3 % 2) // -1
print(3 % -2) // 1

溢出运算符

  • 默认情况下,当给一个整数赋超过他容量的值时,swift会报错而不是生成一个无效的数,这给我们操作过大过小的数提供了额外的安全性。
  • 提供三个溢出运算符让系统支持整数溢出运算:
    • 溢出加法:&+
    • 溢出减法:&-
    • 溢出乘法:&*
 var a: UInt8 = 0xff
 a &+= 1
 print(a) // 0
 a = 0
// a -= 1  error
 a &-= 1
 print(a) // 255

合并空值运算符

  • a ?? b: a有值则解包,如果是nil,则返回默认值b。
  • 文档要求: 表达式a为可选类型,表达式b必须与a的存储类型相同。(实际操作则不然)
  • a ?? b 等价于 a != nil ? a! : b
  • 如果a的值不是nil,那么b的值不会被考虑。
 //正常用法
 var a : Int? = nil
 var b: Int = 255

 print(a ?? b ) // 255
 a = 20
 print(a ?? b ) // 20
 print(type(of: a ?? b)) // Int

 //这样不会报错,但是明显c和d类型不同
 var c: Int? = nil
 let d: String = "abc"
 print(c ?? d) // abc

 //Expression type '(_) -> _' is ambiguous without more context
// print(type(of: c ?? d))

区间运算符

  • 闭区间: (a...b)
  • 半开区间: (a..<b)
  • 单侧区间: (a...) 或者 (...b)
  • 倒叙区间: (a...b).reversed()

位运算

  • 按位取反:~
  • 与: &
  • 或:|
  • 异或:^
  • 移位: >> 和 <<

常见应用:

  • 交换两个变量的值
func swap(_ a: inout Int,_ b: inout Int) {
    a = a ^ b
    b = a ^ b
    a = a ^ b
 }
 var a = 10
 var b = 8
 print("a = \(a) b = \(b)")
 swap(&a, &b)
 print("a = \(a) b = \(b)")
  • 无符号二进制中1的个数
 func countOfOnes(_ num: UInt) -> UInt {
    var count: UInt = 0
    var temp = num
    while temp != 0 {
        count += 1
        temp = temp & (temp - 1)
    }
    return count
 }
  • 判断无符号整型是否是2的整数次幂
 func isPowerOfTwo(_ num: UInt) -> Bool {
    return num & (num - 1) == 0
 }
  • 缺失的数字
 func findLostNum(_ nums: [UInt]) -> UInt {
    return nums.reduce(0) { $0 ^ $1}
 }
  • 缺失的两个数字: 分组异或
func findTwoLostNumbers(_ nums: [UInt]) -> (UInt,UInt) {
    let temp = nums.reduce(0) { $0 ^ $1}
    var flag: UInt = 1
    while temp & flag == 0 {
        flag <<= 1
    }
    var ans: (UInt,UInt) = (0,0)
    for num in nums {
        if num & flag == 0 {
            ans.0 ^= num
        } else {
            ans.1 ^= num
        }
    }
    return ans
 }

运算符重载

  • 类和结构体可以为现有运算符提供自定义实现,叫做运算符重载。
 struct Vector2D {
    var x: Int
    var y: Int
    var description: String {
        return "(x: \(x), y: \(y))"
    }
 }

 extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
 }

 let vector = Vector2D(x: 1, y: 2)
 let another = Vector2D(x: 2, y: 5)
 let combineVector = vector + another
 print("vector1: \(vector.description) another: \(another.description) combineVector: \(combineVector.description)")
 //vector1: (x: 1, y: 2) another: (x: 2, y: 5) combineVector: (x: 3, y: 7)
  • 一元运算符重载: 需要声明是前缀还是后缀(prefix or postfix)
 extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        return Vector2D(x: -vector.x, y: -vector.y)
    }
 }

 let positive = Vector2D(x: 3, y: 5)
 let negative = -positive
 print("positive: \(positive.description) negative: \(negative.description)") //positive: (x: 3, y: 5) negative: (x: -3, y: -5)
  • 组合赋值运算符重载:左参数需要为 inout类型
 extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left.x += right.x
        left.y += right.y
    }
 }

 var original = Vector2D(x: 2, y: 3)
 let toAdd = Vector2D(x: 4, y: 5)
 original += toAdd
 print(original.description) //(x: 6, y: 8)
  • 等价运算符的重载:需要遵循Equatable协议,swift为以下自定义类型提供等价运算符的合成实现。

    • 只拥有遵循Equatable协议的存储属性的结构体
    • 只拥有遵循Equatable协议的关联类型的枚举
    • 没有关联类型的枚举
 extension Vector2D: Equatable {
    static func == (left: Vector2D, right: Vector2D) -> Bool {
        return left.x == right.x && left.y == right.y
    }
 }

自定义运算符

  • 用operator声明
  • 用prefix、postfix、infix限定
 prefix operator ++
 extension Vector2D {
    static prefix func ++ (vector: inout Vector2D) {
        vector += vector
    }
 }
var vector = Vector2D(x: 1, y: 2)
 ++vector
 print("vector: \(vector.description)") //vector: (x: 2, y: 4)
  • 自定义中缀运算符需要制定优先级和结合性: 优先级组
    • 使用系统优先级组
 infix operator +- : AdditionPrecedence
 extension Vector2D {
    static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y - right.y)
    }
 }

 infix operator *^: MultiplicationPrecedence
 extension Vector2D {
    static func *^ (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x * right.x, y: left.y * left.y + right.y * right.y)
    }
 }

 let first = Vector2D(x: 1, y: 2)
 let second = Vector2D(x: 3, y: 7)
 let third = Vector2D(x: 2, y: 2)
 //根据优先级组:先算 *^ 再算 +-
 print("vector: \((first +- second *^ third).description)") //vector: (x: 7, y: -51)
* 使用自定义优先级组
infix operator +- : AdditionPrecedence
 extension Vector2D {
    static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y - right.y)
    }
 }
  precedencegroup UserDefinePrecedence {
     associativity: left
     lowerThan: AdditionPrecedence
  }
 infix operator *^: UserDefinePrecedence
 extension Vector2D {
    static func *^ (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x * right.x, y: left.y * left.y + right.y * right.y)
    }
 }
 
 let first = Vector2D(x: 1, y: 2)
 let second = Vector2D(x: 3, y: 7)
 let third = Vector2D(x: 2, y: 2)
 //根据优先级组:先算 +- 再算 *^
 print("vector: \((first +- second *^ third).description)") //vector: (x: 8, y: 29)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容