引入
函数是执行特定任务的独立代码块。 你可以给函数起一个名字, 以确定它的功能, 这个名字在需要时用来 “调用 ”函数以执行其任务。
Swift 中的每个函数都有一个类型, 由函数的参数类型和返回类型组成。 你可以像使用 Swift 中的其他类型一样使用该类型, 这样就可以轻松地将函数作为 参数传递给其他函数, 并从函数中返回函数。 还可以在其他函数中编写函数, 以便 在嵌套函数作用域中封装有用的功能。
定义和调用函数
定义函数时, 可以选择定义一个或者多个命名、类型化的值, 作为函数的输入参数。
还可以选择定义函数完成后输出传回的值类型, 即返回类型。
每个函数都有一个名字, 用来描述函数执行的任务。 要使用一个函数, 需要使用函数 名来“调用”该函数, 并向其传入函数参数相匹配的输入, 参数的顺序要和定义时一致。
/*
func是定义函数的关键字
greet是自定义的函数名
函数名后面圆括号中定义参数,这个函数有名为person的参数,类型是String
参数后面 -> 跟的是返回值的类型
圆括号中具体就是函数的功能代码
*/
func greet(person: String) -> String {
let greeting = "Hello, \(person)!"
return greeting
}
// 使用 函数名(参数) 的方式调用函数
// 当我们调用函数时,就会将值传入函数,执行函数的逻辑
print(greet(person: "hzclog")) // Hello, hzclog!
print(greet(person: "alice")) // Hello, alice!
函数参数和返回值
函数参数和返回值的定义非常灵活, 从无参数的简单函数, 到多参数带返回值的函数都可以。
无参数函数
定义函数时, 圆括号中什么都没有, 就是无参数的函数。 下面这个连返回值都省了。
func sayHello() {
print("hello!")
}
sayHello() // hello!
多个参数的函数
在函数名后面的圆括号内, 可以加入多个参数, 参数之间使用逗号分隔。
func printInfo(name: String, age: Int, address: String) {
print("姓名: \(name)")
print("年龄: \(age)")
print("地址: \(address)")
}
printInfo(name: "hzclog", age: 18, address: "陕西")
运行结果
姓名: hzclog 年龄: 18 地址: 陕西
上面两个示例代码都没有返回值, 返回值不是必须的。 没有返回值的函数就不需要 -> 和 类型了。
严格来说, 即便没有定义返回值, 函数依旧会返回一个空的元组。
多返回值函数
可以使用元组类型, 返回多个值。
func calculate(a: Int, b: Int) -> (sum: Int, product: Int) {
let sum = a + b
let product = a * b
return (sum, product)
}
let result = calculate(a: 5, b: 2)
print(result.sum)
print(result.product)
运行结果
7 10
隐式返回
如果函数主体是一个表达式, 这个函数就隐式返回该表达式的结果。
func formatInfo(name: String, age: Int, address: String) -> String {
"姓名: \(name), 年龄: \(age), 地址: \(address)"
}
print(formatInfo(name: "hzclog", age: 20, address: "shanxi"))
运行结果
姓名: hzclog, 年龄: 20, 地址: shanxi
函数参数标签和参数名称
每个函数参数都有标签和名称。 参数标签在调用函数时使用, 参数名称用于函数的实现。
默认情况下, 参数使用名称作为参数标签。
指定参数标签
在参数名称之前写参数标签, 使用空格分隔。
// name和age在函数内部使用
func formatInfo(userName name: String, userAge age: Int) -> String {
"姓名: \(name), 年龄: \(age)"
}
// userName和userAge在调用时使用
print(formatInfo(userName: "hzclog", userAge: 20))
运行结果
姓名: hzclog, 年龄: 20
省略参数标签
如果不需要参数标签, 可以使用 _
代替明确的参数标签。
// _ 表示第一个参数不需要标签
func formatInfo(_ name: String, userAge age: Int) -> String {
"姓名: \(name), 年龄: \(age)"
}
// 第一个参数直接传参就行
print(formatInfo("hzclog", userAge: 20))
运行结果
姓名: hzclog, 年龄: 20
默认参数值
在参数类型后面为参数赋值, 可以定义默认值。 如果定义了默认值, 调用时不传参就 使用默认值; 传参就使用传入的值。
将没有默认值的参数放在前面, 有默认值的放在后面。 因为无默认值的参数意义更重要。
// age默认值0
func formatInfo(_ name: String, age: Int = 0) -> String {
"姓名: \(name), 年龄: \(age)"
}
// 使用默认值
print(formatInfo("hzclog")) // 姓名: hzclog, 年龄: 0
// 传入值
print(formatInfo("hzclog", age: 30)) // 姓名: hzclog, 年龄: 30
可变参数
可变参数可以接受零个或多个指定类型的值。 这样就可以在调用函数时传入任意数量的参数。
要定义可变参数, 在参数类型后面加 ...
。
func sum(_ numbers: Int...) -> Int {
var total = 0
for number in numbers {
total += number
}
return total
}
print(sum(1,2,3,4,5)) // 15
print(sum(1,2)) // 3
输入输出参数
如果想要在函数中修改参数的值, 并且这些修改在函数调用结束以后依旧生效, 那就要使用in-out参数。
将inout关键字放在参数类型之前, 就定义了in-out参数。
只能传递变量作为in-out参数, 不能传递常量和字面量, 因为它们是不能修改的。 要将变量作为参数
传递, 需要在变量名前加 &
。
func swapInt(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var intA = 10
var intB = 20
swapInt(&intA, &intB)
print(intA, intB) // 20 10
函数类型
每个函数都有特定的函数类型, 有参数类型和返回类型组成。
比如下面这两个函数, 它们的函数类型都是 (Int, Int) -> Int
。
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
如果没有参数, 没有返回值, 函数类型就是 () -> Void
。
func printHelloWorld() {
print("hello, world")
}
使用函数类型
我们可以像使用其他类型一样, 定义一个变量或常量, 类型定义为函数类型, 就可以为它分配合适的函数了。
var mathFunction: (Int, Int) -> Int = addTwoInts
// 调用
print("Result: \(mathFunction(2, 3))")
只要类型相同, 也可以赋值其他匹配的函数
mathFunction = multiplyTwoInts
函数类型的参数
可以使用函数类型作为另一个函数的参数类型。 这样就可以在调用函数时, 将实现的某些方面留给调用者。
// 加法函数
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// 乘法函数
func multi(_ a: Int, _ b: Int) -> Int {
return a * b
}
// 计算输出函数
// 第一个参数是函数类型,由调用者传入计算方式函数
func printResult(_ mathFunc: (Int, Int) -> Int, _ a: Int, _ b: Int) {
// 使用调用者给出的函数,计算结果并输出
print(mathFunc(a, b))
}
printResult(add, 3, 5) // 计算3+5
printResult(multi, 10, 11) // 计算10*11
函数类型返回值
可以将函数类型作为另一个函数的返回类型。 只要在返回函数的 -> 后面跟上函数类型即可。
// 增
func incre(_ number: Int) -> Int {
return number + 1
}
// 减
func decre(_ number: Int) -> Int {
return number - 1
}
// 根据布尔值,决定返回增函数还是减函数
func change(_ isIncre: Bool = true) -> (Int) -> Int {
isIncre ? incre : decre
}
let number = 1
// 调用change,得到了具体的处理函数incre或者decre,然后再次调用得到结果
print(change()(number)) // 2
print(change(false)(number)) // 0
嵌套函数
之前编写的函数都是全局函数, 因为是在全局范围定义的。
如果在函数内部定义函数, 就是嵌套函数。 嵌套函数对外部是隐藏的, 可以被外层函数调用。 如果将嵌套的函数返回, 就可以在外部使用嵌套函数了。
将上面的代码改成嵌套函数的例子看看。
func change(_ isIncre: Bool = true) -> (Int) -> Int {
// 将incre和decre定义在函数change的内部
// 这两个函数就可以被change使用,但是change以外不能直接使用
func incre(_ number: Int) -> Int {
return number + 1
}
func decre(_ number: Int) -> Int {
return number - 1
}
// 除非return返回嵌套的函数,比如这里做的事情
return isIncre ? incre : decre
}
// 对于这个位置的代码来说,只能使用change,不能直接使用incre和decre
let number = 1
print(change()(number)) // 2
print(change(false)(number)) // 0