Swift函数

目录

引入

函数是执行特定任务的独立代码块。 你可以给函数起一个名字, 以确定它的功能, 这个名字在需要时用来 “调用 ”函数以执行其任务。

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