Swift第二日

今日的学习内容主要集中在运算符及字符串

我就不提哪些个

+ - * / %

?:

|| && ! ==

运算符。

这些基本所有的编程语言都会有,我主要学习一些没怎么见过的,例如,空合运算符,区间运算符

空合运算符

空合运算符效果和JavaScript中的 || 类似,先看一个JavaScript中的例子

const defaultOpt = '1';
let userOpt;
let opt = userOpt || defaultOpt; 

对于JavaScript来说, userOpt 变量会强制转换为Boolean类型,所以上述代码其实约等于以下代码

const defaultOpt = '1';
let userOpt;
let opt = userOpt !== undefined ? userOpt : defaultOpt; // 当然不完全等于这句代码,只是效果类似 

Swift的空合运算符效果和这个类似

let defaultOpt = "1"
var userOpt: String?
let opt = userOpt ?? defaultOpt 

等同于一下代码

let defaultOpt = "1"
var userOpt: String?
let opt = userOpt != nil ? userOpt : defaultOpt 

区间运算符

在shell和python中都遇到过区间运算符,使用过后,只能说真香。然后写JS的时候就会开始抱怨,为什么JS连这个语法都没有,渣渣!

虽然区间运算符循环区间值非常有用,类似下面这种:

for index in 1...9 {
 print(index)
} 

但是个人觉得还是在数组中的应用比较常用:

let arr = ["jeff", "jack", "jackson"];

for item in arr[2...] {
   print(item)
} 

我们可以循环任意连续区间的数组的值

let arr = ["jeff", "jack", "jackson", "jojo"];

for item in arr[..<p>还有👆这种写法,代码简洁多了</p><p>这里的区间运算符和数学的区间表示法类比起来</p><table class="lake-table" style><colgroup><col width="360"><col width="360"></colgroup><tbody><tr><td><p>运算符</p></td><td><p>数学表示</p></td></tr><tr><td><p>...8</p></td><td><p>[0, 8]</p></td></tr><tr><td><p>..<8</p></td><td><p>[0, 8)</p></td></tr><tr><td colspan="1" style="background-color: #FFFFFF;"><p>4...8</p></td><td colspan="1" style="background-color: #FFFFFF;"><p>[4, 8]</p></td></tr><tr><td colspan="1" style="background-color: #FFFFFF;"><p>4..<8</p></td><td colspan="1" style="background-color: #FFFFFF;"><p>[4, 8)</p></td></tr><tr><td colspan="1" style="background-color: #FFFFFF;"><p>2...</p></td><td colspan="1" style="background-color: #FFFFFF;"><p>[2,)</p></td></tr></tbody></table><p><strong>我在JavaScript中几乎不用高级运算符,因为暂时没有去挖掘它们的妙用而且可读性不高,所以一直没去学习</strong></p>

### 位运算符 

<p>位运算符是在二进制层面上操作数值,其中包括 <code>~</code>  (取反)<code>&amp;</code> (按位与), <code>|</code> (按位或), <code>^</code> (按位异或), <code>&lt;&lt;</code> (按位左移), <code>&gt;&gt;</code> (按位右移)</p><p>其实都非常容易理解,我们盗用几张图来解释</p><p style="text-align: center;"><img alt="image.png" title="image.png" src="https://cdn.nlark.com/yuque/0/2019/png/171793/1558488408453-f9c7e006-c16b-4978-8e0a-b9f7892c4645.png#align=left&display=inline&height=130&name=image.png&originHeight=259&originWidth=894&size=18137&status=done&width=447" style="max-width: 600px; width: 447px;"><span> </span></p><p style="text-align: center;"><span>取反</span></p><p>即对所有比特位进行取反</p>

``` swift 
var b2: Int8 = 0b1111111
var inserveB2 = ~b2
print(inserveB2) // -128 

image.png

按位与

按位与需要两个值,通过与操作计算出一个新的值,与操作是当两个位的值都为1时才会计算得1

var b3 = 0b1111100
var b4 = -0b0000111
print(b3 & b4) // 0b1111000 

image.png

按位或

按位或也需要两个值,不过它是计算两个对应比特位的值只要有一个为1时,结果的对应位就为1

var b3 = 0b1111100
var b4 = -0b0000111
print(b3 | b4) // -0b0000011 

image.png

按位异或

按位异或与上述两种计算模式均不同,只有在两个对应位的值不同时,才会计算得1

var b3 = 0b1111100
var b4 = -0b0000111
print(b3 ^ b4) // -0b1111011 

学过高中物理电路的人,似乎对这些操作很熟悉,没错,这就是高中电路学中学到的 或非门  , 异或门 等等。其实两者是相通的。

而对于移位符的操作,则更加简单

如果是左移运算符,那么全体比特位向左移;如果是右移运算符,则全体往右移。

不过移位符当中的补位有些规则得提下:

移位规则实际分两种:无符号整数及有符号整数

以下是无符号整数的规则:

  • 已存在的位按指定的位数进行左移和右移。
  • 任何因移动而超出整型存储范围的位都会被丢弃。
  • 0 来填充移位后产生的空白位。

而对于有符号整数,由于负数是以补位码的形式存储,所以

当对有符号整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用 0

下面介绍一些从网上得来的位运算符的妙用:

  1. 分离rgb
var color = 0xCC6699
var redComponent = (color & 0xFF0000) >> 16
print(String(redComponent, radix: 16))
var greenComponent = (color & 0x00FF00) >> 8
print(String(greenComponent, radix: 16))
var blueComponent = (color & 0x0000FF)
print(String(blueComponent, radix: 16)) 
  1. 判断奇偶数
(a & 1) == 0 
  1. 枚举(应该会很好用)
var FLAG_A = 0b0001
var FLAG_B = 0b0010
var FLAG_C = 0b0100
var FLAG_D = 0b1000 

若同时命中FLAG_A, FLAG_B,我们可以这样判断

var flag = 0b0011
if (FLAG_A | FLAG_B == flag) {
   print("命中")
} 

这样我们可以做到值的多选

运算符函数及自定义运算符

和C++一样,Swift提供重载运算符和自定义运算符的功能

重载运算符意味着可以定义已有的运算符在自定义数据结构当中的操作。这个功能说实话挺黑科技的,而且对于一些库的封装很有好处。

以矢量计算为例

struct Vector2D {
   var x = 0.0, y = 0.0
}

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: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一个新的 Vector2D 实例,值为 (5.0, 5.0) 

+ 操作符是中缀操作符,还可以定义前缀或者后缀操作符

extension Vector2D {
   static 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) 的 Vector2D 实例
let alsoPositive = -negative
// alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例 

另外还支持赋值复合运算符,等价运算符的重载,这里不展开说明

另外一个黑科技是自定义运算符,虽然这个特性我估计用到的不多,不过可以学习一下:

在swift中自定义运算符很简单,只要声明一下运算符即可(当然要带上运算符类型)

prefix operator +++ 

prefix是前缀的意思,(当然还有 postfix , infix ),然后我们只要像重载运算符一样在数据结构中定义运算符实现函数即可

另外Swift还可以定义运算符的优先级

infix operator +-: AdditionPrecedence 

例如上述中缀运算符我们将其归类到AdditionPrecedence优先组中。

还有很多其他的优先组,具体可以参考运算符定义