函数 Functions

Swift中,每个函数都有一个由函数的参数值类型和返回值类型组成的类型。可以把函数类型当做任何其他
普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数
的定义可以写在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。

函数的定义与调用

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

定义一个函数,以func为前缀,定义了一个输入参数一个叫personString值,用->来指定函数返回类型一个String类型返回值

print(_:separator:terminator:)这个函数第一个参数为_即并没有设置标签,其他参数设置了标签并且有默认值因此是可选的

函数参数与返回值

在函数参数名称为_即不设置名称

无参函数

func sayHelloWorld() -> String {
    return "hello, world"
}
print(sayHelloWorld())
// 打印 "hello, world"

注意:
即使这个函数没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号与C语言函数相同

多参函数

func greet(person: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        return greetAgain(person: person)
    } else {
        return greet(person: person)
    }
}
print(greet(person: "Tim", alreadyGreeted: true))
// 打印 "Hello again, Tim!"

传递多个参数,之前用,隔开,这个函数与前面函数虽然名称相同 但是参数不同因此是不同的两个函数

重载:指两个函数的函数名相同,函数的参数列表不同(包括参数个数和参数类型),至于返回类型可同可不同
OC不完全支持重载,即OC支持参数个数不同的函数重载,Swift是完全支持重载的

无返回值函数

func greet(person: String) {
    print("Hello, \(person)!")
}
greet(person: "Dave")
// 打印 "Hello, Dave!"

虽然没有定义返回值,其实返回了一个特殊的Void值,这是一个空的元组,没有任何元素,可以写为()

多重返回值函数

我们可以用元组让多个值作为一个复合值从函数中返回

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// 打印 "min is -6 and max is 109"

注意:

在返回元组时不需要给元组命名,因为它们的名字在函数返回类型中就已经确定了

可选元组返回值

如果函数返回的元组类型有可能整个元组都“没有值”,可以使用可选元组返回类型来说明元组可能为nil

注意:

可选元组类型如 (Int, Int)? 与元组包含可选类型如 (Int?, Int?) 是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。

对于上面的数组我们添加一个空数组检查,当传入的数组为空时返回nil:

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

//用可选类型绑定来检查函数返回的是一个存在的元组还是一个nil
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}
// 打印 "min is -6 and max is 109"

函数参数标签和参数名称

每个函数参数都有一个参数标签以及一个参数名称.
参数标签在调用函数的时候使用:调用时需要将函数的参数标签写在对应参数前
参数名称在函数的实现中使用。默认,函数参数使用参数名称来作为参数标签

指定参数标签

在参数名称前指定它的参数标签,中间以空格分隔:

//参数标签使函数在调用时更有表达力,保持可读性
//argumentLabel参数标签  parameterName参数名称
func someFunction(argumentLabel parameterName: Int) {
    // 在函数体内,parameterName 代表参数值
}
func greet(person: String, from hometown: String) -> String {
    return "Hello \(person)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))

忽略参数标签

我们用下划线来忽略 不设置参数标签

func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
    // 在函数体内,firstParameterName 和 secondParameterName 代表参数中的第一个和第二个参数值
}
someFunction(1, secondParameterName: 2)

如果一个参数有标签,那么在调用时必须使用标签来标记这个参数

默认参数值

可以在函数体重通过给参数赋值来为任意一个参数定义默认值. 如果定义了默认值,可以在调用这个函数时忽略这个参数:

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    // 如果你在调用时候不传第二个参数,parameterWithDefault 会值为 12 传入到函数体中。
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault = 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault = 12

我们通常将不带默认值的参数放在函数参数列表最前。因为一般没有默认值参数更重要,将不带默认值的参数放在最前保证在函数调用时,非默认参数的顺序是一致的,同时也使得相同的函数在不同情况下调用时显得更为清晰。

可变参数

通过在变量类型名后加上...的方式来定义可变参数
可变参数可以接受零个或多个值,我们用其来指定函数传入不确定数量的输入值

我们在函数体中将可变参数传入值变为数组来使用

func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// 返回 3.0, 是这 5 个数的平均数。
arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是这 3 个数的平均数。

注意:

一个函数最多只能有一个可变参数

输入输出参数

参考Swift中的指针操作及使用

函数参数默认为常量,因此在函数体中修改会导致编译错误。如果想要在函数中修改某个参数值,并且这些修改在函数调用后仍然有效,那么需要把这个参数定义为输入输出参数

在参数定义前加上inout关键字来定以输入输出参数,这个值被函数修改然后被传出函数替换原来的值.

只能传递变量给输入输出参数,不能传入常量或字面量,调用时当传入的参数作为输入输出参数时,需要在参数名前加&表示这个值可被修改

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 打印 "someInt is now 107, and anotherInt is now 3"

可以参考C语言中函数的 指针参数

注意:

输入输出参数不能有默认值,而且可变参数不能用inout标记

函数类型

每个函数都有 由函数的参数类型返回类型 组成函数类型

func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}

函数类型为(Int, Int) -> Int

func printHelloWorld() {
    print("hello, world")
}

函数类型为() -> Void

函数类型的使用

Swift中使用函数类型和使用其他类型一样。

var mathFunction: (Int, Int) -> Int = addTwoInts

定义一个类型是‘一个有两个Int型的参数并返回一个Int型的值的mathFunction变量,指向addTwoInts函数

函数类型与其他类型一样可以通过赋值让Swift推断其函数类型

函数类型作为参数类型

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}

//调用
printMathResult({ (a, b) -> Int in
    return a*b
}, 10, 20)

printMathResult(_:_:_:)函数的作用就是输出另一个适当类型的数学函数的调用结果。它不关心传入函数是如何实现的,只关心传入的函数是不是一个正确的类型。这使得printMathResult(_:_:_:)能以一种类型安全(type-safe)的方式将一部分功能转给调用者实现。

函数类型作为返回类型

我们可以将函数类型作为另一个函数的返回类型

func stepForward(_ input: Int) -> Int {
    return input + 1
}
func stepBackward(_ input: Int) -> Int {
    return input - 1
}

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}

//执行
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero 现在指向 stepBackward() 函数。

上面例子获取一个整形接近0应该用的函数

print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

嵌套函数

前面定义的所有函数都为全局函数
我们可以把函数定义在其他函数中,这就是嵌套函数
默认情况下,嵌套函数是对外界不可见的,但是可以被它们的外围函数(enclosing function)调用。一个外围函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用。

OC中是不支持在一个函数中定义另外一个函数

我们用返回嵌套方式重写chooseStepFunction(backward:)函数

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    //我们在函数中定义另外函数
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    //返回嵌套函数
    return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!