7.3 条件语句
我们总会遇到这样的需求:
如果下雨就打雨伞,如果出太阳就打遮阳伞,其他情况不打伞
或者
成绩在90分以上的学生获得优秀奖,在80分以上的获得出色奖,在60以上的获得努力奖,其他的被批评。
这就是条件,事情的发生很多都是有条件的。Swift 提供了两种条件语句,if
和switch
。if
适合简单的情况,比如:
男生请左转,女生请右转
而 switch
适合相对复杂的情况,比如上面根据分数评等级的例子。
If
if
会执行条件语句
,如果是true
则执行一段代码,否则跳过。如
var temperatureInCelsius = 16
if temperatureInCelsius <= 20 {
println("It's a little cold. Consider wearing a scarf.")
}
这个例子判断了当前的天气,如果低于5摄氏度,就输出这句温馨的关怀。
当然可以更复杂一些:
var temperaturInCelsius = 24
if temperaturInCelsius <= 20 {
println("It's a little cold. Consider wearing a scarf.")
} else {
println("It's not that cold. Wear a t-shirt.")
}
这段代码多了一个else
分句,该分句就是我们平时说的,否则。这段代码首先判断了当前的温度,如果低于20提示你要带一条围巾,否则穿一件t-shirt。
还可以更复杂一些:
var temperaturInCelsius = 32
if temperaturInCelsius <= 20 {
println("It's a little cold. Consider wearing a scarf.")
} else if temperaturInCelsius >= 30 {
println("It's really warm. Don't forget to wear sunscreen.")
} else {
println("It's not that cold. Wear a t-shirt.")
}
可见我们可以连续使用if
、else if
、else
,表达一个完整的意思。如果上面的一个if
或else if
的条件语句
为true
那么就会进入该if
或else if
的代码区域执行代码,之后跳出整个if
,而不会继续执行后面的 else if
。
如果男生就向左转,否则向右转:
var gender = "Male"
if gender == "Male" {
println("Turn left")
} else {
println("Turn right")
}
如果下雨就打雨伞,如果出太阳就打遮阳伞,其他情况不打伞
var weather = "sunny"
if weather == "sunny" {
println("Take a bumbersoll with you")
} else if weather == "rainy" {
println("Take an umbrella with you")
}
成绩在90分以上的学生获得优秀奖,在80分以上的获得出色奖,在60以上的获得努力奖,其他的被批评。
var score = 100
if score >= 90 {
println("Excellent")
// 代码执行完这句话,不会继续执行下面的判断,而是跳出整个if
} else if score >= 80 {
println("Good")
} else if score >= 60 {
println("OK)
} else {
println("Work harder")
}
Switch
switch
用于较复杂的情况。switch
的标准格式如下:
switch VALUE_TO_TEST {
case VALUE_1:
ACTIONS_TO_VALUE_1
case VALUE_2,
VALUE_3:
ACTIONS_TO_VALUE_2_OR_VALUE_3
default:
ACTIONS_IF_NOT_MATCHED
}
每个switch
语句都是由多情况断组成的,每个情况由关键字case
开始,case
后是进入这个情况的条件。除了由case
开始的情况外,还有由default
开始的,称之为默认情况。每个情况都必须相应的代码段,而不能为空。switch
根据VALUE_TO_TEST
的值,判断该进入哪个情况执行该情况,执行相应的代码。
每个switch
语句都需要是完备的。就是说每一个VALUE_TO_TEST
的可能值,都必须出现在一个情况中。如果由case
引导的情况没有成功匹配VALUE_TO_TEST
的值,那么就会进入默认情况。如果由case
引导的情况不是完备的,则必须包含默认情况。
下面的例子判断了一个字母是否为元音:
let someCharacter: Character = "e"
switch someCaracter {
case "a", "e", "i", "o", "u":
println("\(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":
println("\(someCharacter) is a consonant")
default:
println("\(someCharacter) is not a vowel or a consonant")
}
不同情况间不会穿越
与 C 和 Objective-C 不同,Swift 的switch
的情况不会穿越,除非显式的指出我们需要穿越。
例如下面这段代码,Swift 编译器会报错:
let anotherCharacter: Character = "a"
switch anotherCharacher {
case "a":
case "A":
println("The letter A")
default:
println("Not the letter A")
}
在 C 中,无论"a"
或者"A"
都会输出"The letter A"
,并且还会继续输出"Not the letter A"
; 但是在 Swift 中,编译器会报错。这种情况我们应该使用 Swift 提供的多值匹配:
let anotherCharacter: Character = "a"
switch anotherCharacher {
case "a", "A":
println("The letter A")
default:
println("Not the letter A")
}
后面章节会介绍关键字
fallthrough
,该关键字可以使Swift的情况穿越
范围匹配
当我们判断一个数字的范围时,多值匹配变得力不从心。例如我们想根据银河中的星星个数输出一句文字描述其数字的范围:
let count = 3_000_000_000_000
let countedThings = "stars in the Milky Way"
var naturalCount: String
switch count {
case 0:
naturalCount = "no"
case 1...3:
naturalCount = "a few"
case 4...9:
naturalCount = "several"
case 10...99:
naturalCount = "tens of"
case 100...999:
naturalCount = "hundreds of"
case 1000...999_999:
naturalCount = "thousands of"
default:
naturalCount = "millions and millions of"
}
println("There are \(naturalCount) \(countedThings).")
元组匹配
Swift 的switch
支持同时匹配多个值,使用元组将其组合起来,成为一个元组变量,传入switch
进行比较。比较两个元组时,有时我们只想比较其中某个位置的值,此时我们可以用下划线忽略不需要比较的位置。
这回我们用直角坐标系来举个例子。我们在坐标系中绘制一个边长为4的正方形,其四个定点分别在(2,2)、(2,-2)、(-2,-2)以及(-2,2)。然后我们判断该坐标系中的一个点是处于原点、坐标轴上还是我们的方框内。
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
println("(0, 0) is at the origin")
case (_, 0):
println("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
println("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
println("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
println("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
当somePoint
同时满足多个情况时,第一个满足的情况会被执行,而其他的都会被跳过。
值绑定
switch
情况的条件语句可以进行值绑定,可以将值绑定到一个作用域为该情况代码段的常量或变量上。
还是用直角坐标系举例:
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
println("on the x-axis with an x value of \(x)")
case (0, let y):
println("on the y-axis with a y value of \(y)")
case let (x, y):
println("somewhere else at (\(x), \(y))")
}
switch
语句判断anotherPoint
是否在坐标轴上,如果是输出其非0的坐标。
该例中第一个情况绑定了anotherPoint
的横坐标,并将其纵坐标被限制为0,任何纵坐标为 0 的点都会进入这种情况,包含原点和其他 x 轴上的点。横坐标被绑定在名为x
的常量上;第二个情况绑定了纵坐标,并限制横坐标为0,除了原点外,所有 y 轴上的点都被匹配到这个情况中;而其他的所有不在坐标轴上的点都进入了第三个情况,我们分别绑定了该点的横纵坐标。
因为所有以case
引导的情况已经可以完备的表达点在直角坐标系的全部情况,所以不需要default
了。
Where
switch
的情况,除了可以进行值匹配以外还可以通过where
子句进行附加条件判断。
我们还用直角坐标系举例,我们想判断一个点是否在直角坐标系的对角线上。
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
println("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
println("(\(x), \(y)) is on the line x == -y")
case let (x, y):
println("(\(x), \(y)) is just some arbitrary point")
}
三种情况都进行了值绑定,绑定后我们又进行了进一步条件判断。第一个情况中,我们判断该点的横纵坐标是否相同,这样的点在一三象限的角平分线上;第二个情况中,我们判断改点的横纵坐标是否相反,这样的点在二四象限的角平分线上;其余的情况通过不带where
子句的值绑定匹配。