Swift系列二十六 - 模式匹配
Posted 1024星球
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift系列二十六 - 模式匹配相关的知识,希望对你有一定的参考价值。
一、字面量(Literal)
了解模式之前,我们先看下什么是字面量。
var age = 10
var isShow = false
var name = "Jack"
上面代码中的10
,false
,"Jack"
就是字面量。
1.1. 字面量类型
常见字面量的默认类型:
public typealias IntegerLiteralType = Int
public typealias FloatLiteralType = Double
public typealias BooleanLiteralType = Bool
public typealias StringLiteralType = String
可以通过typealias
修改字面量的默认类型(一般也没必要修改):
public typealias FloatLiteralType = Float
public typealias IntegerLiteralType = UInt8
var age = 10 // UInt8类型
var height = 20.0 // Float类型
Swift自带的绝大部分类型,都支持直接通过字面量进行初始化(不需要直接调用初始化器):
Bool、Int、Float、Double、String、Array、Dictionary、Set、Optional等
1.2. 字面量协议
Swift自带类型之所以能够通过字面量初始化,是因为它们遵守了对应的协议。
Bool : ExpressibleByBooleanLiteral
Int : ExpressibleByIntegerLiteral
Float、Double : ExpressibleByIntegerLiteral、ExpressibleByFloatLiteral
Dictionary : ExpressibleByDictionaryLiteral
String : ExpressibleByStringLiteral
Array、Set : ExpressibleByArrayLiteral
Optinal : ExpressibleByNilLiteral
示例代码:
var b: Bool = false // ExpressibleByBooleanLiteral
var i: Int = 10 // ExpressibleByIntegerLiteral
var f0: Float = 10 // ExpressibleByIntegerLiteral
var f1: Float = 10.0 // ExpressibleByFloatLiteral
var d0: Double = 10 // ExpressibleByIntegerLiteral
var d1: Double = 10.0 // ExpressibleByFloatLiteral
var s: String = "idbeny" // ExpressibleByStringLiteral
var arr: Array = [1, 2, 3] // ExpressibleByArrayLiteral
var set: Set = [1, 2, 3] // ExpressibleByArrayLiteral
var dict: Dictionary = ["name" : "daben"] // ExpressibleByDictionaryLiteral
var o: Optional<Int> = nil // ExpressibleByNilLiteral
1.3. 字面量协议应用
示例代码一:
Bool
类型直接赋值给Int
类型的变量,会直接报错:
如果要不报错,只需要给Int
添加一个遵守协议的扩展即可:
extension Int : ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = value ? 1 : 0
}
}
var num: Int = true
print(num) // 输出:1
示例代码二:
class Student: ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral, CustomStringConvertible {
var name: String = ""
var score: Double = 0
required init(floatLiteral value: Double) {
self.score = value
}
required init(integerLiteral value: Int) {
self.score = Double(value)
}
required init(stringLiteral value: String) {
self.name = value
}
// 支持Unicode和特殊字符
required init(unicodeScalarLiteral value: String) {
self.name = value
}
required init(extendedGraphemeClusterLiteral value: String) {
self.name = value
}
var description: String {
"name=\\(name), score=\\(score)"
}
}
var stu: Student = 90
print(stu) // 输出:name=, score=90.0
stu = 98.5
print(stu) // 输出:name=, score=98.5
stu = "idbeny"
print(stu) // 输出:name=idbeny, score=0.0
示例代码三:
struct Point {
var x = 0.0, y = 0.0
}
extension Point : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
init(arrayLiteral elements: Double...) {
guard elements.count > 0 else {
return
}
self.x = elements[0]
guard elements.count > 1 else {
return
}
self.y = elements[1]
}
init(dictionaryLiteral elements: (String, Double)...) {
for (k, v) in elements {
if k == "x" {
self.x = v
} else if k == "y" {
self.y = v
}
}
}
}
var p: Point = [5.12, 10.24]
print(p) // 输出:Point(x: 5.12, y: 10.24)
p = ["x" : 11, "y" : 22]
print(p) // 输出:Point(x: 11.0, y: 22.0)
二、模式(Pattern)
模式是用于匹配的规则,比如switch
的case
、捕捉错误的catch
、if/guard/while/for
语句的条件等。
Swift中的模式有:
- 通配符模式(Wildcard Pattern)
- 标识符模式(Identifier Pattern)
- 值绑定模式(Value-Binding Pattern)
- 元组模式(Tuple Pattern)
- 枚举Case模式(Enumeration Pattern)
- 可选模式(Optional Pattern)
- 类型转换模式(Type-Casting Pattern)
- 表达式模式(Expression Pattern)
2.1. 通配符模式
_
匹配任何值_?
匹配非nil
值
示例代码:
enum Life {
case human(name: String, age: Int?)
case animal(name: String, age: Int?)
}
func check(_ life: Life) {
switch life {
case .human(let name, _):
print("human", name)
case .animal(let name, _?):
print("animal", name)
default:
print("other")
}
}
check(.human(name: "Rose", age: 20)) // 输出:human Rose
check(.human(name: "Jack", age: nil)) // 输出:human Jack
check(.animal(name: "Dog", age: 5)) // 输出:animal Dog
check(.animal(name: "Cat", age: nil)) // 输出:other
case .human(let name, _):
中的_
就是匹配任意值。
case .animal(let name, _?):
中的_?
就是匹配非nil
值。
2.2. 标识符模式
给对应的变量、常量名赋值。
var age = 10
let name = "idbeny"
2.3. 值绑定模式
把对应位置的值绑定到变量/常量上。
let point = (3, 2)
switch point {
case let (x, y):
print("x:\\(x), y:\\(y)")
}
// 输出:x:3, y:2
2.4. 元组模式
本质也是值绑定和通配符模式。
示例代码一(数组):
let points = [(0, 0), (1, 0), (2, 0)]
for (x, _) in points {
print(x)
}
/*
输出 :
0
1
2
*/
示例代码二(case):
let name: String? = "idbeny"
let age = 18
let info: Any = [1, 2]
switch (name, age, info) {
case (_?, _, _ as String):
print("case")
default:
print("default")
}
// 输出:default
示例代码三(字典):
var scores = ["jack" : 98, "rose" : 100, "kate" : 86]
for (name, score) in scores {
print(name, score)
}
/*
输出:
jack 98
kate 86
rose 100
*/
2.5. 枚举Case模式
if case
语句等价于只有1个case
的switch
语句。
示例代码一:
let age = 2
func test() {
// 原来的写法
if age >= 0 && age <= 9 {
print("[0, 9]")
}
// 枚举case模式
if case 0...9 = age {
print("[0, 9]")
}
guard case 0...9 = age else { return }
print("[0, 9]")
}
test()
/*
输出:
[0, 9]
[0, 9]
[0, 9]
*/
if case 0...9 = age
可以理解为是拿出age
的值和case
后面的条件进行匹配。
下面的代码完全等价上面示例代码的if case
:
switch age {
case 0...9:
print("[0, 9]")
default:
break
}
// 输出:[0, 9]
示例代码二:
let age = 2
let ages: [Int?] = [2, 3, nil, 5]
for case nil in ages {
print("有nil值")
break
}
// 输出:有nil值
示例代码三:
let points = [(1, 0), (2, 1), (3, 0)]
for case let (x, 0) in points {
print(x)
}
// 输出:1 3
// 错误写法:
// for (x, 0) in points {
// print(x)
// }
2.6. 可选模式
示例代码一:
let age: Int? = 42
if case .some(let x) = age {
print(x)
}
// 输出:42
// x?代表非空
if case let x? = age {
print(x)
}
// 输出:42
示例代码二:
let ages: [Int?] = [nil, 2, 3, nil, 5]
for case let age? in ages {
print(age)
}
/*
输出:
2
3
5
*/
上面示例二的代码和下面的代码等效:
for item in ages {
// 可选项绑定
if let age = item {
print(age)
}
}
/*
输出:
2
3
5
*/
示例代码三:
func check(_ num: Int?) {
switch num {
case 2?:
print("2")
case 4?:
print("4")
case 6?:
print("6")
case _?:
print("other")
case _:
print("nil")
}
}
check(4) // 输出:4
check(8) // 输出:other
check(nil) // 输出:nil
2.7. 类型转换模式
主要是is
和as
的用法。
示例代码一:
let num: Any = 6
switch num {
case is Int:
print("is Int", num)
default:
break
}
// 输出:is Int 6
case is Int
仅仅是判断num
是否为Int
类型,编译器不会自动强转,依然认为num
是Any
类型。
如果需要强转并且判断类型,可以使用as
:
switch num {
case let n as Int:
print("as Int", n)
default:
break
}
// 输出:as Int 6
此时的n
是Int
类型,但是num
依然是Any
类型。如果num
不是Int
类型就会跳过当前case
,匹配下一个case
。
示例代码二:
class Animal {
func eat() {
print(type(of: self), "eat")
}
}
class Dog : Animal {
func run() {
print(type(of: self), "run")
}
}
class Cat : Animal {
func jump() {
print(type(of: self), "jump")
}
}
func check(_ animal: Animal) {
switch animal {
case let dog as Dog:
dog.eat()
dog.run()
case is Cat:
animal.eat()
default:
break
}
}
check(Dog())
/*
输出:
Dog eat
Dog run
*/
check(Cat())
/*
输出:
Cat eat
*/
上面的示例中,如果匹配case is Cat
时,怎样才能执行jump
方法?因为animal
是Animal
类型。
可以对animal
进行强制转换:
(animal as? Cat)?.jump()
2.8. 表达式模式
表达式模式用在case
中:
let point = (1, 2)
switch point {
case (0, 0):
print("(0, 0) is at the origin.")
case (-2...2, -2...2):
print("(\\(point.0), \\(point.1)) is near the origin.")
default:
print("The point is at (\\(point.0), \\(point.1)).")
}
// 输出:(1, 2) is near the origin.
通过汇编查看上面示例代码,发现示例程序是用~=
运算符做匹配:
其实,在Swift中,一些复杂
switch
匹配会用到~=
运算符,但并不是所有的switch
都是用到该运算符。
可以通过重载运算符,自定义表达式模式的匹配规则。
2.8.1. 自定义表达式模式
示例代码一:
struct Student {
var score = 0, name = ""
static func ~= (pattern: Int, value: Student) -> Bool {
value.score >= pattern
}
static func ~= (pattern: ClosedRange<Int>, value: Student) -> Bool {
pattern.contains(value.score)
}
static func ~= (pattern: Range<Int>, value: Student) -> Bool {
pattern.contains(value.score)
}
}
使用示例代码一:
var stu = Student(score: 72, name: "idbeny")
switch stu {
case 100: print(">=100")
case 90: print(">=90")
case 80..<90: print("[80, 90)")
case 60...79: print("[60, 79]")
case 0: print(">=0")
default: break
}
// 输出:[60, 79]
stu
是怎么和Int
、Rang
进行匹配的呢?重写~=
运算符。
基本上是固定写法(返回值必须是Bool
):
// pattern: case后面的类型
// value: switch后面的类型
static func ~ROS从入门到精通系列(二十六) 标准化ROS代码风格 - . C++ 风格指南
ROS从入门到精通系列(二十六) 标准化ROS代码风格 - . C++ 风格指南
ROS从入门到精通系列(二十六) 标准化ROS代码风格 - . C++ 风格指南