HR's Blog

Swimming 🏊 in the sea🌊of code!

0%

Pattern matching

.

来至于Ray Wenderlich的《Swift Apprentice》中的Pattern matching章节的笔记。

https://www.raywenderlich.com/134844/pattern-matching-in-swift

Basic pattern matching

if and guard

匹配元组

1
2
3
4
5
6
7
8
9
func process(point: (x: Int, y:Int, z:Int)) -> String {
if case (0, 0, 0) = point {
return "At origin"
}
return "Not at origin"
}

let point = (x:0, y:0, z:0)
let response = process(point: pint)
1
2
3
4
5
6
func process(point: (x: Int, y:Int, z:Int)) -> String {
guarde case (0, 0, 0) = point else {
return "Not at origin"
}
return "At origin"
}

Switch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let closeRange = -2...2
let midRange = -5...5

switch point {
case (0, 0, 0):
return "At origin"
case (closeRange, closeRange, closeRange):
return "Nearby origin"
case (midRnage, midRnage, midRnage):
return "Not ear origin"
}

let point = (x:15, y:5, z:3)
let response = process(point: point) //Not near origin

Switch可以匹配多种情况,Switch可以匹配范围。
找到第一个匹配值以后,Switch会退出。

for

1
2
3
4
let groupSizes = [1, 5, 4,6, 2, 1, 3]
for case 1 in groupSize {
print("Found an individual")
}

Patterns

Wildcard pattern

检查点是不是在x轴上。

1
2
3
if case (_, 0, 0) = coordinate {
print ("on the x-axis")
}

Value-binding pattern

检查点是不是在x轴上面,并且得到x的值。

1
2
3
if case (let x, 0, 0) = coordinate {
print("On the x-axis at \(x)")
}

对于你要得到多个值的情况,你可以直接把let写在括号外,switch获得枚举值的时候,也是可以把let或者var写在外面。

1
2
3
if case let (x, y, 0) = coordinate {
print("On the x-y plane at (\(x), \(y))")
}

Enumeration case pattern

1
2
3
4
5
6
7
8
9
10
enum Direction {
case north, south, east, west
}

let heading = Direction.north

if case .north = heading {
print("Don't forget your jacket")
}

这个也可以用在处理错误的时候,传递相关的错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Organism {
case plant
case animal(legs: Int)
}

let pet = Organism.animal(legs: 4)

switch pet {
case .animal(let legs):
print("ok")
default:
print("ok")
}

Optional pattern

当数组包含Optional的值,可以通过这种syntactic sugar来获取里面的值并且解包。

1
2
3
for case let name? in names {
print(name)
}

“Is” type-casting pattern

用is检查是不是特定的类型,多用在JSON的解析。

1
2
3
4
5
6
7
8
9
10
let array: [Any] = [15, "George", 2.0]

for element in array {
switch element {
case is String:
print("ok")
default:
print("123")
}
}

“As” type-casting pattern

当element是string类型,运行下面语句。并将值赋值给text变量

1
2
3
4
5
6
7
8
for element in array {
switch element {
case let text as String:
print("Found a string:\(text)")
default:
print("Found something else")
}
}

Advanced patterns

###Qualifying with where
传入的值可以被2整除

1
2
3
4
5
6
7
8
for number in 1...9 {
switch number {
case let x where x % 2 == 0:
print("even")
default:
print("odd")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
enum LevelStatus {
case complete
case inProgress(percent: Double)
case notStarted
}

let leves: [LevelStatus] = [.complete, .inProgress(percent:0.9), .notStarted]

for level in levels {
switch level {
case .inProgress(let percent) where percent > 0.8 :
print("Almost there!")
case .inProgress(let percent) where percent > 0.5 :
print("Halfway there!")
case .inProgress(let precent) where percent > 0.2 :
print("Made it throug the beginning!")
default:
break
}
}

Chaining with commas

用都好匹配多个值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func timeOfDayDescription(hour: Int) -> String {
switch hour {
case 0, 1, 2, 3, 4, 5:
return "Early morning"
case 6, 7, 8, 9, 10, 11:
return "Morning"
case 12, 13, 14, 15, 16:
return "Afternoon"
case 17, 18, 19:
return "Late evening"
default:
return "INVALID HOUR!"
}
}

首先匹配pet是不是动物,然后在匹配这个动物的腿是不是在2…4之间。

1
2
3
4
5
if case .animal(let legs) = pet, case 2...4 = legs {
print("potentially cuddly") //Printed!
} else {
print("no change for cuddles")
}

Swiftif申明,用途相当广

  • Simple logical test foot == 10 || bar > baz
  • Optional binding let foot = maybeFoo
  • Pattern matching cas .bar(let something) = theValue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum Number {
case integerValue(Int)
case doubleValue(Double)
case booleanValue(Bool)
}

let a = 5
let b = 6
let c : Number? = .integerValue(7)
let d: Number? = .integerValue(8)

if a != b {
if let c = c {
if let d = d {
if case .integervalue(let cValue) = c {
if case .integerValue(let dValue) = d {
if dValue > cValue {
print("some code")
}
}
}
}
}
}

Swift中我们可以简写成如下代码,和guard功能类似,有可以把if金字塔式的结构给流水化。下面的代码只要有一个不满足,那么if就不成立。

1
2
3
4
5
6
7
8
if a !=b,
let c = c,
let d = d,
case .integervalue(let cValue) = c,
case .integerValue(let dValue) = d,
dValue > cValue {
print("some code")
}

Custom tuple

1
2
3
4
5
6
let name = "Bob"
let age = 23

if case ("Bob", 23) = (name, age) {
print()
}
1
2
3
4
5
6
7
8
9
10
11
12
var username: String?
var password: String?
switch (username, password) {
case let (username?, password?):
print("Success")
case let (username?, nil):
print("Password is missing")
case let (nil, password):
print ("Username is missing")
case (nil, nil):
print("Both username and password are missing")
}

Fun with wildcards

Do something multiple times

1
2
3
for _ in 1...3 {
print("hi")
}

_意味着你不关心里面的值是什么。

Validate that an optional exists

1
2
3
4
let user: String? = "Bob"
guard let _ = user else {
print("There is no user.")
}

检查optional是否有值。

1
2
3
guard user != nil else {
print("There is no user.")
}

Organize an if-else-if

switch代替在OC中的if else的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Rectangle {
let width: Int
let height: Int
let color: String
}

let view = Rectangle(width:15, height:60, color:"Green")
switch view {
case _ where view.height < 50:
print("Shrter thant 50 units")
case _ where view.width > 20:
print("Over 50 tall, & over 20 wide")
case _ where view.color = "Green":
print ("Over 50 tall, at most 20 wide, & green")
default:
print("This view can't be described by this example")
}

Programming exercises

Fibonacci斐波那契数列

斐波那契数列每个数字都是前面两个数字之和例如0,1,1,2,3,5,8…
通过传入position的值,算出该位置的值。

1
2
3
4
5
6
7
8
9
10
func fibonacci(position: Int) -> Int {
switch position {
case let n where n <= 1 :
return 0 //当是第一位的时候返回0
case 2:
return 1
case let n:
return fiboncci(position: n -1 ) + fibonacci(position: n- 2)
}
}

这个例子也是避免在Switch中使用default的例子,因为let n已经包含所有的值,所以default就不需要在写。

FizzBuzz

从1-100中,当是3的倍数的时候打印Fizz,当是5的倍数的时候返回Buzz,当既是5的倍数也是3的倍数的时候返回FizzBuzz

1
2
3
4
5
6
7
8
9
10
11
12
for i in 1...100 {
switch (i %3, i%5) {
case (0, 0):
print("FizzBuzz")
case (0, _):
print("Fizz")
case (_, 0):
print("Buzz")
case (_, _):
print(i)
}
}

Expression pattern

当你要检查Int的值是不是在一个范围内的时候,这时候就要用~=符号来判断

1
2
3
4
5
let matched = (1...10 ~= 5)   //true

if case 1...10 = 5 {
print("Int the range")
}

Overloading~=

如果你要自己实现~=例如,提供数组[0, 1, 2, 3]然后检查2是不是在该数组中,如果直接写以下代码会报错

1
2
3
4
5
6
7
8
9
10
let list = [0, 1, 2, 3]
let integer = 2
let isInArray = (list ~= integer) //Error!

if case list = integer { //Error
print("The integer is in the array")
} else {
print("The integer is not in the array")
}

重写~=方法

1
2
3
4
5
6
7
8
func ~=(pattern: [Int], value: Int) -> Bool {
for i in pattern {
if i == value {
return true
}
}
return false
}