语法(一) – 模式匹配

温馨提示:本文最后更新于2024-07-23 15:52:44,某些文章具有时效性,若有错误或已失效,请在下方留言

Swift 的 switch 语句具有灵活却富有表现力的匹配模式。

基本使用

单值匹配

单值字符串匹配

let name = "twostraws"

switch name {
case "bilbo":
    print("Hello, Bilbo Baggins!")
case "twostraws":
    print("Hello, Paul Hudson!")
default:
    print("Authentication failed")
}

多值匹配

多值匹配主要是通过元组实现匹配

let name = "twostraws"
let password = "fr0st1es"

switch (name, password) {
case ("bilbo", "bagg1n5"):
    print("Hello, Bilbo Baggins!")
case ("twostraws", "fr0st1es"):
    print("Hello, Paul Hudson!")
default:
    print("Who are you?")
}

如果您愿意,可以将两个值合并为一个元组,然后进行匹配。

let authentication = (name: "twostraws", password: "fr0st1es")

switch authentication {
case ("bilbo", "bagg1n5"):
    print("Hello, Bilbo Baggins!")
case ("twostraws", "fr0st1es"):
    print("Hello, Paul Hudson!")
default:
    print("Who are you?")
}

在这种情况下,元组的两个部分都必须与 case 相匹配才能执行。

部分匹配

在处理元组时,有时只需要部分匹配:只关心某些值,但不关心其他值。在这种情况下,使用下划线表示 "任何值都可以"。

let authentication = (name: "twostraws", password: "fr0st1es", ipAddress: "127.0.0.1")

switch authentication {
case ("bilbo", "bagg1n5", _):
    print("Hello, Bilbo Baggins!")
case ("twostraws", "fr0st1es", _):
    print("Hello, Paul Hudson!")
default:
    print("Who are you?")
}

Swift会使用它找到的第一个匹配的case,因此您需要确保首先查找最具体的内容。

如果你想匹配元组的一部分,但仍想知道其它部分是什么,可以使用 let 语法。

switch authentication {
case ("bilbo", "bagg1n5", _):
    print("Hello, Bilbo Baggins!")
case ("twostraws", let password, _):
    print("Hello, Paul Hudson“your password was \(password)!")
default:
    print("Who are you?")
}

匹配计算元组

元组和其他数据结构一样,可以使用动态代码来创建。当你想将元组中的值范围缩小到一个较小的子集,从而只需要少量的 case 语句时,这一点尤其有用。

func fizzbuzz(number: Int) -> String {
    switch (number % 3 == 0, number % 5 == 0) {
    case (true, false):
        return "Fizz"
    case  (false, true):
        return "Buzz"
    case (true, true):
        return "FizzBuzz"
    case (false, false):
        return String(number)
    }
}

print(fizzbuzz(number: 5)) // Buzz

循环

使用元组的一部分进行模式匹配非常简单:可以告诉 Swift 应该匹配什么,使用 let 将一个值绑定到一个局部常量,或者使用 _ 来表示你并不关心某个值是什么。

在处理循环时,可以设置只有当项目符合我们指定条件时,菜对其循环。从一个基本例子开始:

let twostraws = (name: "twostraws", password: "fr0st1es")
let bilbo = (name: "bilbo", password: "bagg1n5")
let taylor = (name: "taylor", password: "fr0st1es")

let users = [twostraws, bilbo, taylor]

for user in users {
    print(user.name)
}

这会创建一个元组数组,然后在每个元组上循环并打印其 name 值。

可以在元组中使用 case 来匹配元组中的特定值。

for case ("twostraws", "fr0st1es") in users {
    print("User twostraws has the password fr0st1es")
}

使用相同的语法将本地常量绑定到每个元组的值上

for case (let name, let password) in users {
    print("User \(name) has the password \(password)")
}

通常情况下,最好还是将 let 重新排列成这样:

for case let (name, password) in users {
    print("User \(name) has the password \(password)")
}

将这两种方法结合在一起

for case let (name, "fr0st1es") in users {
    print("User \(name) has the password \"fr0st1es\"")
}

这将过滤 users 数组,以便循环中只使用密码为 "fr0st1es "的项目。

匹配可选值

.some 和 .none

使用 .some.none 来匹配 "有值 "和 "无值"。

let name: String? = "twostraws"
let password: String? = "fr0st1es"

switch (name, password) {
case let(.some(matchedName), .some(matchedPassword)):
    print("Hello, \(matchedName)")
case let(.some(matchedName), .none):
    print("Please enter a password.")
default:
    print("Who are you?")
}

?和 nil

使用 ? 进行可选值的匹配,问号的作用与可选链的作用类似:只有找到值才继续。

let name: String? = "twostraws"
let password: String? = "fr0st1es"

switch (name, password) {
case let(name?, password?):
    print("Hello, \(name)")
case let(username?, nil):
    print("Please enter a password.")
default:
    print("Who are you?")
}

这两种方法在 for case let 代码中同样有效。

import Foundation
let data: [Any?] = ["Bill", nil, 69, "Ted"]

for case let .some(datum) in data {
    print(datum)
}

for case let datum? in data {
    print(datum)
}

匹配范围

条件语句

使用模式匹配运算符 ~= 进行范围匹配。

let age = 36

if 0..<18 ~= age {
    print("You have the energy and time, but not the money")
} else if 18 ..< 70 ~= age {
    print("You have the energy and money, but not the time")
} else {
    print("You have the time and money, but not the energy")
}

0 ..< 18 会创建一个 Range 结构的实例,而 Range 结构有自己的方法集,它的 contains() 方法特别有用。

let age = 36
if (0..<18).contains(age) {
    print("You have the energy and time, but not the money")
} else if (18 ..< 70).contains(age) {
    print("You have the energy and money, but not the time")
} else {
    print("You have the time and money, but not the energy")
}

可以将这种范围匹配与现有的元组匹配代码结合起来

let user = (name: "twostraws", password: "fr0st1es", age: 36)

switch user {
case let (name, _, 0 ..< 18):
    print("\(name) has the energy and time, but no money")
case let (name, _, 18 ..< 70):
    print("\(name) has the money and energy, but no time")
case let (name, _, _):
    print("\(name) has the time and money, but no energy")
}

匹配枚举和关联值

基本枚举匹配

enum WeatherType {
    case cloudy
    case sunny
    case windy
}

let today = WeatherType.cloudy

switch today {
case .cloudy:
    print("It's cloudy")
case .sunny:
    print("It's windy")
case .windy:
    print("It's sunny")
}

枚举关联值匹配

忽略关联值

enum WeatherType {
    case cloudy(coverage: Int)
    case sunny
    case windy
}

let today = WeatherType.cloudy(coverage: 100)

switch today {
case .cloudy:
    print("It's cloudy")
case .sunny:
    print("It's windy")
case .windy:
    print("It's sunny")
}

使用这种方法,实际的 switch 代码将保持不变。

使用关联值


enum WeatherType {
    case cloudy(coverage: Int)
    case sunny
    case windy
}

let today = WeatherType.cloudy(coverage: 100)

switch today {
case .cloudy(let coverage):
    print("It's cloudy with \(coverage)% coverage")
case .sunny:
    print("It's windy")
case .windy:
    print("It's sunny")
}

关联值与范围匹配

enum WeatherType {
    case cloudy(coverage: Int)
    case sunny
    case windy
}

let today = WeatherType.cloudy(coverage: 100)

switch today {
case .cloudy(let coverage) where coverage == 0:
    print("You must live in Death Valley")
case .cloudy(let coverage) where (1...50).contains(coverage):
    print("It's a bit cloudy, with \(coverage)% coverage")
case .cloudy(let coverage) where (51...99).contains(coverage):
    print("It's very cloudy, with \(coverage)% coverage")
case .cloudy(let coverage) where coverage == 0:
    print("You must live in the UK")
case .windy:
    print("It's sunny")
default:
    print("it's sunny")
}

如果要在循环中匹配关联值,可以使用 case let 关键字。

let forecast: [WeatherType] = [.cloudy(coverage: 40), .sunny, .windy, .cloudy(coverage: 100), .sunny]

for case let .cloudy(coverage) in forecast {
    print("It's cloudy with \(coverage)% coverage")
}

使用 where 关键字

可以使用 where 删除可选项

let celebrities: [String?] = ["Michael Jackson", nil, "Michael Caine", nil, "Michael Jordan"]

for name in celebrities where name != nil {
    print(name)
}

这当然可行,但它对循环中字符串的可选性没有做任何处理,因此会打印出这样的结果:

Optional("Michael Jackson")
Optional("Michael Caine")
Optional("Michael Jordan")

相反,应使用 for case let 来处理可选性,并使用 where 来关注过滤值。

for case let name? in celebrities {
    print(name)
}

运行时, name 将只包含有值的字符串,因此其输出将是

Michael Jackson
Michael Caine
Michael Jordan


《pro swift》

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容