官网链接
Swift
中的每个函数都有一个类型,由函数的参数类型和返回类型组成。 您可以像Swift
中的任何其他类型一样使用此类型,这使得将函数作为参数传递给其他函数以及从函数返回函数变得很容易。 函数也可以在其他函数中编写,以在嵌套函数范围内封装有用的功能。
-
定义和调用函数 (Defining and Calling Functions)
每个函数都有一个函数名称,它描述了函数执行的任务。 要使用函数,可以使用其名称“调用”该函数,并将其传递给与函数参数类型匹配的输入值(称为参数)。 必须始终以与函数参数列表相同的顺序提供函数的参数。
以下示例中的函数称为greet(person :)
,因为它的作用是 - 它将一个人的姓名作为输入并返回该人的问候语。 要实现此目的,您需要定义一个输入参数 - 一个名为person
的String
值 - 以及一个返回类型的String
,它将包含该人的问候语:
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
所有这些信息都汇总到函数的定义中,该定义以func
关键字为前缀。 使用返回箭头指示函数的返回类型 - >
(连字符后跟右尖括号),后面跟着要返回的类型的名称。
该定义描述了函数的功能,期望接收的内容以及完成后返回的内容。 该定义使得从代码中的其他位置明确调用函数变得容易:
print(greet(person: "Anna"))
// Prints "Hello, Anna!"
print(greet(person: "Brian"))
// Prints "Hello, Brian!"
通过在person
参数标签之后传递一个String
值来调用greet(person :)
函数,例如greet(person:“Anna”)
。 因为函数返回一个String
值,所以greet(person :)
可以包含在print(_:separator:terminator :)
函数的调用中,以打印该字符串并查看其返回值,如上所示。
要使此函数的主体更短,可以将消息创建和return
语句组合成一行:
func greetAgain(person: String) -> String {
return "Hello again, " + person + "!"
}
print(greetAgain(person: "Anna"))
// Prints "Hello again, Anna!"
-
函数参数和返回值 (Function Parameters and Return Values)
1.没有参数的函数
定义输入参数不需要函数。这是一个没有输入参数的函数,它总是在调用时返回相同的字符串消息:
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world"
函数定义在函数名称后仍需要括号,即使它不带任何参数。 调用函数时,函数名后面还会有一对空括号。
2.具有多个参数的函数
函数可以有多个输入参数,这些参数写在函数的括号内,用逗号分隔。
func greet(person: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted {
return greetAgain(person: person)
} else {
return greet(person: person)
}
}
print(greet(person: "Tim", alreadyGreeted: true))
// Prints "Hello again, Tim!"
3.没有返回值的函数
定义返回类型不需要函数。 这是greet(person :)
函数的一个版本,它打印自己的String
值而不是返回它:
func greet(person: String) {
print("Hello, \(person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"
因为它不需要返回值,所以函数的定义不包括返回箭头( - >
)或返回类型。
NOTE
严格地说,即使没有定义返回值,这个版本的greet(person :)
函数仍会返回一个值。 没有定义返回类型的函数返回Void
类型的特殊值。 这只是一个空元组,写成()
。
调用函数时,可以忽略函数的返回值:
func printAndCount(string: String) -> Int {
print(string)
return string.count
}
func printWithoutCounting(string: String) {
let _ = printAndCount(string: string)
}
printAndCount(string: "hello, world")
// prints "hello, world" and returns a value of 12
printWithoutCounting(string: "hello, world")
// prints "hello, world" but does not return a value
4.具有多个返回值的函数
您可以使用元组类型作为函数的返回类型,以将多个值作为一个复合返回值的一部分返回。
下面的示例定义了一个名为minMax(array :)
的函数,该函数查找Int
值数组中的最小和最大数字:
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1.. currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
minMax(array :)
函数返回一个包含两个Int
值的元组。 这些值标记为min
和max
,以便在查询函数的返回值时可以通过名称访问它们。
因为元组的成员值被命名为函数返回类型的一部分,所以可以使用点语法访问它们以检索最小和最大找到的值:
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// Prints "min is -6 and max is 109"
请注意,元组的成员不需要在从函数返回元组的位置进行命名,因为它们的名称已经指定为函数返回类型的一部分。
5.可选的元组返回类型
如果要从函数返回的元组类型可能对整个元组具有“无值”,则可以使用可选的元组返回类型来反映整个元组可以为零的事实。 您通过在元组类型的右括号后面放置一个问号来编写一个可选的元组返回类型,例如(Int,Int)?
或(String,Int,Bool)?
NOTE
一个可选的元组类型,如(Int,Int)?
与包含可选类型的元组不同,例如(Int?,Int?)
。 使用可选的元组类型,整个元组是可选的,而不仅仅是元组中的每个单独值。
上面的minMax(array :)
函数返回一个包含两个Int值的元组。 但是,该函数不会对传递的数组执行任何安全检查。 如果数组参数包含空数组,则如上所定义的minMax(array :)
函数将在尝试访问数组[0]
时触发运行时错误。
要安全地处理空数组,请使用可选的元组返回类型编写minMax(array :)
函数,并在数组为空时返回值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.. currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
您可以使用可选绑定来检查此版本的minMax(array :)
函数是否返回实际的元组值或nil
:
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
print("min is \(bounds.min) and max is \(bounds.max)")
}
// Prints "min is -6 and max is 109"
-
函数参数标签和参数名称 (Function Argument Labels and Parameter Names)
每个函数参数都有参数标签和参数名称。 调用函数时使用参数标签; 每个参数都写在函数调用中,其前面带有参数标签。 参数名称用于函数的实现。 默认情况下,参数使用其参数名称作为其参数标签。
func someFunction(firstParameterName: Int, secondParameterName: Int) {
// In the function body, firstParameterName and secondParameterName
// refer to the argument values for the first and second parameters.
}
someFunction(firstParameterName: 1, secondParameterName: 2)
所有参数必须具有唯一名称。 虽然多个参数可能具有相同的参数标签,但唯一的参数标签有助于使您的代码更具可读性。
1.指定参数标签
您在参数名称前面编写参数标签,用空格分隔:
func someFunction(argumentLabel parameterName: Int) {
// In the function body, parameterName refers to the argument value
// for that parameter.
}
这是一个greet(person :)
函数的变体,它接受一个人的姓名和家乡并返回一个问候语:
func greet(person: String, from hometown: String) -> String {
return "Hello \(person)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill! Glad you could visit from Cupertino."
参数标签的使用可以允许以表达式,类似句子的方式调用函数,同时仍然提供可读且清晰的函数体。
2.省略参数标签
如果您不想要参数的参数标签,请为该参数写下划线(_
)而不是显式参数标签。
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// In the function body, firstParameterName and secondParameterName
// refer to the argument values for the first and second parameters.
}
someFunction(1, secondParameterName: 2)
3.默认参数值
您可以通过在该参数的类型之后为参数赋值来为函数中的任何参数定义默认值。 如果定义了默认值,则可以在调用函数时省略该参数。
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// If you omit the second argument when calling this function, then
// the value of parameterWithDefault is 12 inside the function body.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12
4.变量参数
可变参数接受零个或多个指定类型的值。 使用可变参数指定在调用函数时可以向参数传递不同数量的输入值。 通过在参数的类型名称后插入三个句点字符(...
)来写入可变参数。
传递给可变参数的值在函数体内可用作适当类型的数组。 例如,一个名为数字和一个Double ...
类型的可变参数在函数体内可用作一个名为[Double]
类型的常量数组。
下面的示例计算任意长度的数字列表的算术平均值(也称为平均值):
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)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
NOTE
函数可以具有至多一个可变参数。
5.输入输出参数
您只能传递一个变量作为输入输出参数的参数。不能传递常量或文字值作为参数,因为不能修改常量和文字。当您将变量作为参数传递给in-out
参数时,您可以在变量名称的前面直接放置一个&,以表明它可以被函数修改。
NOTE
输入输出参数不能具有默认值,并且可变参数不能标记为输入。
这是一个名为swapTwoInts(: :)的函数示例,它有两个输入输出的整数参数,称为a和b:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
您可以使用两个Int类型的变量调用swapTwoInts(_:_ :)
函数来交换它们的值。 请注意,当someInt
和anotherInt
的名称传递给swapTwoInts(_:_ :)
函数时,它们的前缀为&
符号:
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"
上面的示例显示someTnt
和anotherInt
的原始值由swapTwoInts(_:_ :)
函数修改,即使它们最初是在函数外部定义的。
NOTE
输入输出参数与从函数返回值不同。 上面的swapTwoInts
示例没有定义返回类型或返回值,但它仍然修改someInt
和anotherInt
的值。 输入输出参数是函数在其函数体范围之外产生效果的另一种方法。
-
函数类型 (Function Types)
每个函数都有一个特定的函数类型,由函数的参数类型和返回类型组成。
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
此示例定义了两个简单的数学函数,称为addTwoInts
和multiplyTwoInts
。 这些函数每个都接受两个Int
值,并返回一个Int
值,这是执行适当的数学运算的结果。
这两个函数的类型是(Int,Int) - > Int
。 这可以理解为:
“具有两个参数的函数,类型为Int
,并返回Int
类型的值。”
这是另一个例子,对于没有参数或返回值的函数:
func printHelloWorld() {
print("hello, world")
}
此函数的类型是() - > Void
,或“没有参数的函数,并返回Void
”。
1.使用函数类型
您可以像使用Swift
中的任何其他类型一样使用函数类型。 例如,您可以将常量或变量定义为函数类型,并为该变量分配适当的函数:
var mathFunction: (Int, Int) -> Int = addTwoInts
“定义一个名为mathFunction
的变量,它的类型为'一个带有两个Int
值的函数,并返回一个Int
值。'设置这个新变量来引用名为addTwoInts
的函数。”
addTwoInts(_:_ :)
函数与mathFunction
变量具有相同的类型,因此Swift
的类型检查器允许这个赋值。
您现在可以使用名称mathFunction
调用指定的函数:
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"
可以将具有相同匹配类型的不同函数分配给相同的变量,方法与非函数类型相同:
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// Prints "Result: 6"
与任何其他类型一样,在将函数赋值给常量或变量时,可以将其保留为Swift
以推断函数类型:
let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int
2.函数类型作为参数类型
您可以使用函数类型如(Int,Int) - > Int
作为另一个函数的参数类型。 这使您可以为函数调用者保留函数实现的某些方面,以便在调用函数时提供。
以下是从上面打印数学函数结果的示例:
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// Prints "Result: 8"
3.函数类型作为返回类型
您可以使用函数类型作为另一个函数的返回类型。 您可以通过在返回函数的返回箭头( - >)之后立即编写完整的函数类型来完成此操作。
下一个示例定义了两个名为stepForward(_ :)
和stepBackward(_ :)
的简单函数。 stepForward(_ :)
函数返回的值比其输入值多1
,stepBackward(_ :)
函数返回的值比其输入值小1
。 这两个函数都有一个(Int) - > Int
类型:
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
这是一个名为chooseStepFunction(backward :)
的函数,其返回类型是(Int) - > Int
。 chooseStepFunction(backward :)
函数返回stepForward(_ :)
函数或stepBackward(_ :)
函数,该函数基于一个名为backward
的布尔参数:
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
上面的示例确定是否需要正或负步骤来将名为currentValue
的变量逐渐接近零。 currentValue的初始值为3
,这意味着currentValue> 0
返回true
,导致chooseStepFunction(backward :)
返回stepBackward(_ :)
函数。 对返回函数的引用存储在名为moveNearerToZero
的常量中。
现在moveNearerToZero
引用了正确的函数,它可以用于计数到零:
print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!
4.嵌套函数
到目前为止,您在本章中遇到的所有函数都是全局函数的示例,这些函数在全局范围内定义。 您还可以在其他函数体内定义函数,称为嵌套函数。
默认情况下,嵌套函数对外界是隐藏的,但它们的封闭函数仍然可以调用它们。 封闭函数也可以返回其嵌套函数之一,以允许嵌套函数在另一个范围内使用。
您可以重写上面的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)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!