Swift学习:2.24 高级操作符
参考Swift 官方教程《The Swift Programming Language》中文版高级运算符除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语言和Objective-C中的位运算符和移位运算。 不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的。溢出行为会被捕获并报告为错误。你是故意的?好吧,你可以使用Swift为你准备的另一套默认允许溢出的数值运算符,如可溢出的加号为 自定义的结构,类和枚举,是否可以使用标准的运算符来定义操作?当然可以!在Swift中,你可以为你创建的所有类型定制运算符的操作。 可定制的运算符并不限于那些预设的运算符,你可以自定义中置,前置,后置及赋值运算符,当然还有优先级和结合性。这些运算符在代码中可以像预设的运算符一样使用,你也可以扩展已有的类型以支持你自定义的运算符。 位运算符位操作符可以操作数据结构中原始数据的每个比特位。位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,位操作符在同外部资源的数据进行交互的时候也很有用,比如在使用用户协议进行通信的时候,运用位运算符来对原始数据进行编码和解码。 Swift支持如下所有C语言的位运算符: 按位取反运算符按位取反运算符 这个运算符是前置的,所以请不加任何空格地写在操作数之前。 let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000
使用按位取反运算 按位与运算符按位与运算符对两个数进行操作,然后返回一个新的数,这个数的每个位都需要两个输入数的同一位都为1时才为1。 以下代码, let firstSixBits: UInt8 = 0b22222100
let lastSixBits: UInt8 = 0b00222221
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
按位或运算按位或运算符 如下代码, let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 22222110
按位异或运算符按位异或运算符 以下代码, let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001
按位左移/右移运算符左移运算符 按位左移和按位右移的效果相当把一个整数乘于或除于一个因子为 无符整型的移位操作对无符整型的移位的效果如下: 已经存在的比特位向左或向右移动指定的位数。被移出整型存储边界的的位数直接抛弃,移动留下的空白位用零 以下这张把展示了 let shiftBits: UInt8 = 4 // 即二进制的00000100
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
你可以使用移位操作进行其他数据类型的编码和解码。 let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC,即 204
let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66,即 102
let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153
这个例子使用了一个 对 然后,按向右移动16位,即 同样的,绿色部分来自于 最后,蓝色部分对 有符整型的移位操作有符整型的移位操作相对复杂得多,因为正负号也是用二进制位表示的。(这里举的例子虽然都是8位的,但它的原理是通用的。) 有符整型通过第1个比特位(称为符号位)来表达这个整数是正数还是负数。 其余的比特位(称为数值位)存储其实值。有符正整数和无符正整数在计算机里的存储结果是一样的,下来我们来看 符号位为 负数呢,跟正数不同。负数存储的是2的n次方减去它的绝对值,n为数值位的位数。一个8比特的数有7个数值位,所以是2的7次方,即128。 我们来看 现在符号位为 负数的编码方式称为二进制补码表示。这种表示方式看起来很奇怪,但它有几个优点。 首先,只需要对全部8个比特位(包括符号)做标准的二进制加法就可以完成 第二,由于使用二进制补码表示,我们可以和正数一样对负数进行按位左移右移的,同样也是左移1位时乘于 对有符整型按位右移时,不使用0填充空白位,而是根据符号位(正数为 这就确保了在右移的过程中,有符整型的符号不会发生变化。这称为算术移位。 正因为正数和负数特殊的存储方式,向右移位使它接近于 溢出运算符默认情况下,当你往一个整型常量或变量赋于一个它不能承载的大数时,Swift不会让你这么干的,它会报错。这样,在操作过大或过小的数的时候就很安全了。 例如, var potentialOverflow = Int16.max
// potentialOverflow 等于 32767,这是 Int16 能承载的最大整数
potentialOverflow += 1
// 噢,出错了
对过大或过小的数值进行错误处理让你的数值边界条件更灵活。 当然,你有意在溢出时对有效位进行截断,你可采用溢出运算,而非错误处理。Swfit为整型计算提供了5个
值的上溢出下面例子使用了溢出加法 var willOverflow = UInt8.max
// willOverflow 等于UInt8的最大整数 255
willOverflow = willOverflow &+ 1
// 此时 willOverflow 等于 0
值的下溢出数值也有可能因为太小而越界。举个例子:
Swift代码是这样的: var willUnderflow = UInt8.min
// willUnderflow 等于UInt8的最小值0
willUnderflow = willUnderflow &- 1
// 此时 willUnderflow 等于 255
有符整型也有类似的下溢出,有符整型所有的减法也都是对包括在符号位在内的二进制数进行二进制减法的,这在 "按位左移/右移运算符" 一节提到过。最小的有符整数是 来看看Swift代码: var signedUnderflow = Int8.min
// signedUnderflow 等于最小的有符整数 -128
signedUnderflow = signedUnderflow &- 1
// 此时 signedUnderflow 等于 127
除零溢出一个数除以0 let x = 1
let y = x / 0
使用它们对应的可溢出的版本的运算符 let x = 1
let y = x &/ 0
// y 等于 0
优先级和结合性运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。 结合性定义相同优先级的运算符在一起时是怎么组合或关联的,是和左边的一组呢,还是和右边的一组。意思就是,到底是和左边的表达式结合呢,还是和右边的表达式结合? 在混合表达式中,运算符的优先级和结合性是非常重要的。举个例子,为什么下列表达式的结果为 2 + 3 * 4 % 5
// 结果是 4
如果严格地从左计算到右,计算过程会是这样:
但是正确答案是 乘法和求余拥有相同的优先级,在运算过程中,我们还需要结合性,乘法和求余运算都是左结合的。这相当于在表达式中有隐藏的括号让运算从左开始。 2 + ((3 * 4) % 5)
3 * 4 = 12,所以这相当于: 2 + (12 % 5)
12 % 5 = 2,所这又相当于 2 + 2
计算结果为 4。 查阅Swift运算符的优先级和结合性的完整列表,请看表达式。
运算符函数让已有的运算符也可以对自定义的类和结构进行运算,这称为运算符重载。 这个例子展示了如何用 例子中定义了一个名为 struct Vector2D {
var x = 0.0,y = 0.0
}
@infix func + (left: Vector2D,right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x,y: left.y + right.y)
}
该运算符函数定义了一个全局的 在这个代码实现中,参数被命名为了 这个函数是全局的,而不是 let vector = Vector2D(x: 3.0,y: 1.0)
let anotherVector = Vector2D(x: 2.0,y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一个新的Vector2D,值为 (5.0,5.0)
这个例子实现两个向量 前置和后置运算符上个例子演示了一个双目中置运算符的自定义实现,同样我们也可以玩标准单目运算符的实现。单目运算符只有一个操作数,在操作数之前就是前置的,如 实现一个前置或后置运算符时,在定义该运算符的时候于关键字 @prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x,y: -vector.y)
}
这段代码为 对于数值,单目减运算符可以把正数变负数,把负数变正数。对于 let positive = Vector2D(x: 3.0,y: 4.0)
let negative = -positive
// negative 为 (-3.0,-4.0)
let alsoPositive = -negative
// alsoPositive 为 (3.0,4.0)
组合赋值运算符组合赋值是其他运算符和赋值运算符一起执行的运算。如 @assignment func += (inout left: 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)
你可以将 @prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D {
vector += Vector2D(x: 1.0,y: 1.0)
return vector
}
这个前置使用了已经定义好的高级加赋运算,将自己加上一个值为 var toIncrement = Vector2D(x: 3.0,y: 4.0)
let afterIncrement = ++toIncrement
// toIncrement 现在是 (4.0,5.0)
// afterIncrement 现在也是 (4.0,5.0)
比较运算符Swift无所知道自定义类型是否相等或不等,因为等于或者不等于由你的代码说了算了。所以自定义的类和结构要使用比较符 定义相等运算符函数跟定义其他中置运算符雷同: @infix func == (left: Vector2D,right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
@infix func != (left: Vector2D,right: Vector2D) -> Bool {
return !(left == right)
}
上述代码实现了相等运算符 现在我们可以使用这两个运算符来判断两个 let twoThree = Vector2D(x: 2.0,y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0,y: 3.0)
if twoThree == anotherTwoThree {
println("这两个向量是相等的.")
}
// prints "这两个向量是相等的."
自定义运算符标准的运算符不够玩,那你可以声明一些个性的运算符,但个性的运算符只能使用这些字符 新的运算符声明需在全局域使用 operator prefix +++ {}
这段代码定义了一个新的前置运算符叫 @prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D {
vector += vector
return vector
}
var toBeDoubled = Vector2D(x: 1.0,y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled 现在是 (2.0,8.0)
// afterDoubling 现在也是 (2.0,8.0)
自定义中置运算符的优先级和结合性可以为自定义的中置运算符指定优先级和结合性。可以回头看看优先级和结合性解释这两个因素是如何影响多种中置运算符混合的表达式的计算的。 结合性(associativity)的值可取的值有 结合性(associativity)的值默认为 以下例子定义了一个新的中置符 operator infix +- { associativity left precedence 140 }
func +- (left: Vector2D,y: left.y - right.y)
}
let firstVector = Vector2D(x: 1.0,y: 2.0)
let secondVector = Vector2D(x: 3.0,y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector 此时的值为 (4.0,-2.0)
这个运算符把两个向量的 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |