目录
按照传统,编程语言教程的第一个程序应该是在屏幕上打印出"Hello, world!"。在 Swift 中,这可以用一行代码实现:
swift
print("Hello, world!")
// 打印"Hello, world!"
如果你了解其他编程语言,那么你应该很熟悉这种写法------在 Swift 中,这行代码就是一个完整的程序。你不需要为了输出文本或处理字符串而导入一个单独的库。全局作用域中的代码会被当做程序的入口点,因此你不需要 main()
函数。你也不需要在每条语句的末尾添加分号。
1、简单值
使用 let
来声明常量,使用 var
来声明变量。常量的值不需要在编译时确定,但是你必须且只能为它赋值一次。这意味着你可以用常量来命名那些只需确定一次但要在多处使用的值。
swift
var myVariable = 42
myVariable = 50
let myConstant = 42
常量或变量的类型必须与要赋予它的值的类型一致。不过,你不必总是显式地写出类型。在声明常量或变量时提供一个值,编译器会自动推断出它的类型。在上面的例子中,因为初始值是整数,所以编译器推断出 myVariable
是整数类型。
如果初始值无法提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分隔。
swift
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
练习: 创建一个常量,显式指定类型为 Float
,并指定值为 4。
值不会被隐式转换为另一种类型。如果你需要把一个值转换成其他类型,需要显式地创建所需类型的实例。
swift
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
练习: 尝试移除最后一行中的 String
类型转换。会显示什么错误?
有一种更简单的方法可以在字符串中包含值:将值写在括号中,并在括号前加上反斜杠(\
)。例如:
swift
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
练习: 使用 \()
将一个浮点计算包含在字符串中,并在一句问候语中包含某人的名字。
对于占用多行的字符串,使用三个双引号("""
)来表示。每行开头的缩进只要与结尾引号的缩进相匹配,都会被移除。例如:
swift
let quotation = """
Even though there's whitespace to the left,
the actual lines aren't indented.
Except for this line.
Double quotes (") can appear without being escaped.
I still have \(apples + oranges) pieces of fruit.
"""
使用方括号([]
)来创建数组和字典,并通过在方括号内写上索引(index)或键(key)来访问它们的元素。最后一个元素后面允许有个逗号。
swift
var fruits = ["strawberries", "limes", "tangerines"]
fruits[1] = "grapes"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
数组在添加元素时会自动变大。
swift
fruits.append("blueberries")
print(fruits)
// 打印"["strawberries", "grapes", "tangerines", "blueberries"]"
你也可以使用方括号来表示空数组或空字典。对于数组,使用 []
;对于字典,使用 [:]
。
swift
fruits = []
occupations = [:]
如果你要将一个空数组或空字典赋值给一个新变量,或者赋值到没有任何类型信息的地方,那么你需要显式指定类型。
swift
let emptyArray: [String] = []
let emptyDictionary: [String: Float] = [:]
2、控制流
使用 if
和 switch
来创建条件语句,使用 for
-in
、while
和 repeat
-while
来创建循环。包裹条件或循环变量的圆括号是可选的,但包裹代码块的花括号是必须的。
swift
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)
// 打印"11"
在 if
语句中,条件必须是一个布尔表达式------这意味着像 if score { ... }
这样的代码是错误的,而不是隐式地将 score
与零进行比较。
你可以在赋值操作符(=
)或 return
之后使用 if
或 switch
,以根据条件选择一个值。
swift
let scoreDecoration = if teamScore > 10 {
"🎉"
} else {
""
}
print("Score:", teamScore, scoreDecoration)
// 打印"Score: 11 🎉"
你可以将 if
和 let
结合使用,来处理值可能缺失的情况。这些值用可选值来表示。一个可选值要么包含一个值,要么包含 nil
来表示值缺失。在一个值的类型后面加一个问号(?
)来标记该值是可选值。
swift
var optionalString: String? = "Hello"
print(optionalString == nil)
// 打印"false"
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
练习: 将 optionalName
的值设置为 nil
,greeting
会是什么?添加一个 else
分支,当 optionalName
是 nil
时,给 greeting
赋一个不同的值。
如果可选值是 nil
,条件会判断为 false
,花括号中的代码将被跳过。如果不是 nil
,会将值解包并赋给 let
后面的常量,这样代码块中就可以使用这个解包后的值了。
另一种处理可选值的方法是使用 ??
运算符提供一个默认值。如果可选值缺失的话,则使用默认值来代替。
swift
let nickname: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickname ?? fullName)"
你可以使用更简洁的写法来解包一个值,解包后的值用同样的名字来表示。
swift
if let nickname {
print("Hey, \(nickname)")
}
// 不会打印任何东西,因为 nickname 为 nil 。
switch
语句支持任意类型的数据和多种比较操作------不仅限于整数和等值比较。
swift
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
// 打印"Is it a spicy red pepper?"
练习: 删除 default
分支,会显示什么错误?
注意 let
在上述例子的匹配模式中是如何使用的,它将匹配到的值赋给常量 x
。
运行 switch
中匹配到的 case
语句之后,程序会退出 switch
语句,并不会继续向下运行,所以不需要在每个子句结尾写 break
。
你可以使用 for-in
来遍历字典,使用一对变量来表示每个键值对。字典是一个无序的集合,所以它们的键和值会以任意顺序遍历完。
swift
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
// 打印"25"
练习: 将 _
替换成变量名,以便记录最大的数字是属于哪一类的。
使用 while
来重复运行一段代码,直到条件改变。循环条件也可以放在末尾,以确保至少循环一次。
swift
var n = 2
while n < 100 {
n *= 2
}
print(n)
// 打印"128"
var m = 2
repeat {
m *= 2
} while m < 100
print(m)
// 打印"128"
练习: 将条件从 m < 100
改为 m < 0
,以观察 while
和 repeat-while
在循环条件一开始就为假时的区别。
你可以在循环中使用 ..<
来创建一个索引范围。
swift
var total = 0
for i in 0..<4 {
total += i
}
print(total)
// 打印"6"
使用 ..<
创建不包含上限值的范围,使用 ...
创建包含上限值的范围。
3、函数和闭包
使用 func
来声明一个函数。在函数名后面加上括号并传入参数列表来调用函数。使用 ->
将参数名称和类型与函数的返回类型分隔开。
swift
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")
练习: 删除参数 day
,再添加一个参数,在欢迎语中包含今天的特价菜。
默认情况下,函数使用参数名作为参数的标签。可以在参数名之前自定义参数标签,或者使用 _
来表示不使用参数标签。
swift
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")
使用元组来创建复合值------例如,用于从函数中返回多个值。元组的元素可以通过名称或编号来引用。
swift
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
varmin = scores[0]
varmax = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} elseif score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
// 打印"120"
print(statistics.2)
// 打印"120"
函数可以嵌套。嵌套函数可以访问外部函数中声明的变量。你可以使用嵌套函数来组织长或复杂的函数代码。
swift
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
函数是一等类型。这意味着函数可以作为另一个函数的返回值。
swift
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
函数可以将另一个函数作为参数传入。
swift
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)
函数实际上是一种特殊的闭包:它是能在之后被调用的代码块。闭包中的代码能访问闭包作用域中的变量和函数,即使闭包在执行时处于不同的作用域中------你在嵌套函数的例子中已经见过这种情况。你可以使用({}
)来创建一个匿名闭包。使用 in
将参数和返回类型与代码主体分隔开。
swift
numbers.map({ (number: Int) -> Int in
let result = 3 * number
return result
})
练习: 重写闭包,使其对所有奇数返回零。
你有多种方法可以更简洁地编写闭包。当闭包的类型已知时,比如作为代理的回调函数,你可以省略参数类型、返回类型,或者两者都省略。对于单个语句的闭包,它会隐式返回该语句的值。
swift
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
// 打印"[60, 57, 21, 36]"
你可以通过编号而不是名称来引用参数------这种方式在非常简短的闭包中特别有用。如果闭包是函数的最后一个参数,它可以直接写在括号后面。当闭包是函数的唯一参数时,甚至可以省略括号。
swift
let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)
// 打印"[20, 19, 12, 7]"