Skip to content

Latest commit

 

History

History
237 lines (188 loc) · 9.15 KB

控制流.md

File metadata and controls

237 lines (188 loc) · 9.15 KB

目录

switch

switch 语句会将一个值与多个可能的模式匹配。然后基于第一个成功匹配的模式来执行合适的代码块。 switch 语句代替if 语句提供了对多个潜在状态的响应

没有隐式贯穿

相比 CObjective-C 里的 switch 语句来说,Swift 里的 switch 语句不会默认从每个情况的末尾贯穿到下一个情况里。相反,整个 switch 语句会在匹配到第一个 switch 情况执行完毕之后退出,不再需要显式的 break 语句。这使得switch 语句比 C 的更安全和易用,并且避免了意外地执行多个 switch 情况

每一个情况的函数体必须包含至少一个可执行的语句。下面的代码就是不正确的,因为第一个情况是空的:

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a":
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// this will report a compile-time error

在一个 switch 情况中匹配多个值可以用逗号分隔,并且可以写成多行,如果列表太长的话

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// Prints "The letter A"

区间匹配

switch情况的值可以在一个区间中匹配。这个栗子使用了数字区间来为语言中的数字区间进行转换:

let approximateCount = 62
let countedThings = "moons orbiting Saturn"
var naturalCount: String
switch approximateCount {
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hundreds of"
default:
    naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
// prints "There are dozens of moons orbiting Saturn."

元组

你可以使用元组来在一个 switch 语句中测试多个值。每个元组中的元素都可以与不同的值或者区间进行匹配。另外,使用下划线(_)来表明匹配所有可能的值

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("(0, 0) is at the origin")
case (_, 0):
    print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
// prints "(1, 1) is inside the box"

值绑定

switch 情况可以将匹配到的值临时绑定为一个常量或者变量,来给情况的函数体使用。这就是所谓的值绑定,因为值是在情况的函数体里"绑定"到临时的常量或者变量的

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("on the x-axis with an x value of \(x)")
case (0, let y):
    print("on the y-axis with a y value of \(y)")
case let (x, y):
    print("somewhere else at (\(x), \(y))")
}
// prints "on the x-axis with an x value of 2"

Where

switch 情况可以使用 where 分句来检查额外的情况

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}
// prints "(1, -1) is on the line x == -y"

switch 语句决定坐标在绿色的斜线 x==y ,还是在紫色的斜线 x == -y ,或者都不是

复合情况

多个 switch 共享同一个函数体的多个情况可以在 case 后写多个模式来复合,在每个模式之间用逗号分隔。如果任何一个模式匹配了,那么这个情况都会被认为是匹配的。如果模式太长,可以把它们写成多行,比如说:

let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
     "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter) is a consonant")
default:
    print("\(someCharacter) is not a vowel or a consonant")
}
// Prints "e is a vowel"

复合情况同样可以包含值绑定。所有复合情况的模式都必须包含相同的值绑定集合,并且复合情况中的每一个绑定都得有相同的类型格式。这才能确保无论复合情况的那部分匹配了,接下来的函数体中的代码都能访问到绑定的值并且值的类型也都相同

let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
    print("On an axis, \(distance) from the origin")
default:
    print("Not on an axis")
}
// Prints "On an axis, 9 from the origin"

控制转移语句

控制转移语句在你代码执行期间改变代码的执行顺序,通过从一段代码转移控制到另一段。Swift 拥有五种控制转移语句:

  • continue
  • break
  • fallthrough
  • return
  • throw

switch里的break

当在switch语句里使用时, break 导致 switch 语句立即结束它的执行,并且转移控制到 switch 语句结束花括号( } )之后的第一行代码上

这可以用来在一个 switch 语句中匹配和忽略一个或者多个情况。因为 Swiftswitch 语句是穷尽且不允许空情况的,所以有时候有必要故意匹配和忽略一个匹配到的情况以让你的意图更加明确。要这样做的话你可以通过把 break 语句作为情况的整个函数体来忽略某个情况。当这个情况通过 switch 语句匹配到了,情况中的 break 语句会立即结束 switch 语句的执行

Fallthrough

Swift 中的 Switch 语句不会从每个情况的末尾贯穿到下一个情况中。相反,整个 switch 语句会在第一个匹配到的情况执行完毕之后就直接结束执行。比较而言,C 你在每一个 switch 情况末尾插入显式的 break 语句来阻止贯穿。避免默认贯穿意味着 Swiftswitch 语句比 C 更加清晰和可预料,并且因此它们避免了意外执行多个 switch 情况

如果你确实需要 C 风格的贯穿行为,你可以选择在每个情况末尾使用 fallthrough 关键字。下面的栗子使用了fallthrough 来创建一个数字的文字描述:

let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " a prime number, and also"
    fallthrough
default:
    description += " an integer."
}
print(description)
// prints "The number 5 is a prime number, and also an integer."

fallthrough 关键字不会为switch情况检查贯穿入情况的条件。 fallthrough 关键字只是使代码执行直接移动到下一个情况(或者 default 情况)的代码块中,就像C的标准 switch 语句行为一样

提前退出

guard 语句,类似于 if 语句,基于布尔值表达式来执行语句。使用 guard 语句来要求一个条件必须是真才能执行guard 之后的语句。与 if 语句不同, guard 语句总是有一个 else 分句—— else 分句里的代码会在条件不为真的时候执行

func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }
    
    print("Hello \(name)!")
    
    guard let location = person["location"] else {
      	// location为空时 执行以下
        print("I hope the weather is nice near you.")
        return
    }
    
    print("I hope the weather is nice in \(location).")
}
 
greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."

如果 guard 语句的条件被满足,代码会继续执行直到 guard 语句后的花括号。任何在条件中使用可选项绑定而赋值的变量或者常量在 guard 所在的代码块中随后的代码里都是可用的

如果这个条件没有被满足,那么在 else 分支里的代码就会被执行。这个分支必须转移控制结束 guard 所在的代码块。要这么做可以使用控制转移语句比如 returnbreakcontinue 或者 throw ,或者它可以调用一个不带有返回值的函数或者方法,比如 fatalError()

相对于使用 if 语句来做同样的事情,为需求使用 guard 语句来提升你代码的稳定性。它会让正常地写代码而不用把它们包裹进 else 代码块,并且它允许你保留在需求之后处理危险的需求