函数是独立的一块执行特定任务的代码。给函数一个确认它做什么的名字,这个名字用来在需要的时候调用函数来执行它的任务。
swift的统一的函数语法非常灵活,从单一没有参数名称C-style的函数到每个参数都有名称和参数标签的复杂的Objective-C-style的方法都可以表示。参数可以提供默认的值来简化函数调用并且可以传入in-out参数,只要函数完成了它的执行就可以修改传入的变量。
swift中的每个函数都有一个类型,由函数参数的类型和返回的类型组成。你可以使用像swift中其他类型一样使用这个类型,也可以从函数中返回函数。函数也可以写在其他函数中在嵌套函数中封装有用的功能。
定义和调用函数(Defining and Calling Functions)
当你定义一个函数时,你可以随意的定义一个或者多个命名的类型的值,函数将这些值作为输入,称作parameters。你也可以随意定义一个值的类型,当函数结束的时候作为输出传回来,成为return type。
每个函数有一个函数名,描述了函数执行的任务。要使用函数,使用它的名字调用那个函数并且传给他输入的值(成为arguments),要与函数的parameters的类型匹配。函数的arguments提供的顺序必需要和函数parameter列表的顺序一样
下面例子中的函数名为greet(person:),因为那是它做的事情--将一个人的名字作为输入并返回了对那个人的招呼。为了实现这个,定义一个输入parameter--一个名为person的String值--和一个String的返回类型,将会包含那个人的招呼:
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}全部的这些信息都包含在函数的定义中,func关键词在其前面。使用return箭头->(被右破折号后面的连字符),返回类型的名字跟在后面。
定义描述了函数做什么,他想要接受什么,当它完成的时候返回什么。定义使你的函数可以在代码中任何地方被清晰地调用:
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:)函数中来打印字符串并且查看它的返回值,像上面展示的一样。
函数greet(person:)的主体定义了一个新的名为greeting的String常量并且分配给他一个简单的greeting信息。这个greeting过后会通过return关键词传到函数外面,函数结束它的调用并且返回greeting当前的值。
可以使用不同的输入值多次调用greet(person:)函数。上面的例子展示了如果使用“Anna”输入值调用会发生什么,和一个“Brian”输入值。每次函数返回独特的greeting。
为了使函数体更短,可以将信息创建和返回语句合并在一行:
func greetAgain(person: String) -> String {
return "Hello again, " + person + "!"
}
print(greetAgain(person: "Anna"))
// Prints "Hello again, Anna!"函数参数和返回值(Function Parameters and Return Values)
在swift中函数参数和返回值是非常灵活的。从一个需要一个单一的未命名的参数的实用函数到使用表达性参数名字和不同参数选项的复杂的函数,你可以定义任何东西。
没有参数的函数(Functions Without Parameters)
函数不一定要定义输入参数。这里是一个没有输入参数的函数,无论何时调用,通常返回一样String的信息:
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world"在函数名后面函数的定义仍然需要括号,即使它不需要任何参数。当函数调用的时候,函数后面也要跟着一对空的括号。
多参数的函数(Functions With Multiple Parameters)
函数可以有多个输入参数,写在函数的括号中,使用逗号分隔。
这个参数需要人的名字和他们是否已经打过招呼了作为输入,并为那个人返回一个合适的greeting:
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!"你可以通过在括号里用逗号分开传给他一个标签为person的String参数值和一个标签为alreadyGreeted的Bool参数来调用函数greeting(person:alreadyGreeted:)。注意这个函数和在之前部分中的greet(person:)函数不一样。即便两个函数的名字都是greet开始,函数greet(person:alreadyGreeted:)使用了两个参数但是greet(person:)function只用了一个。
无返回值的函数(Functions Without Return Values)
函数不一定要定义返回值。这里是一个greet(person)的版本,打印它的String值而不是返回:
func greet(person: String) {
print("Hello, \(person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"以为他不需要返回值,函数定义没有返回箭头(->)或者返回类型。
严格讲,这个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第一个函数,printAndCount(string:),打印了一个string,并返回了Int类型的字符的个数。第二个函数,printWithoutCounting(sting:),调用第一个函数,但是忽略了它的返回值。当第二个函数调用的时候,信息仍然被第一个函数打印,但是没有使用返回值。
返回值可以忽略,但是说了会返回值的函数一定要返回值。一个定义了返回类型的函数不允许没有返回一个值就跳出函数的底部,如果这样做了结果会发生编译时错误。
多个返回值的函数(Functions with Multiple Values)
可以使用元祖作为函数的返回类型来作为一个组合返回值的一部分让函数返回多个值。
下面的例子定义了一个名为minMax(array:)的函数,在数组中查找最小和最大的Int类型的数字:
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)
}函数minMax(array:)返回了含有两个Int类型的元祖。这些值标为min和max,所以当查询函数的返回值时可以通过名字来访问。
函数minMax(array:)的主体开头通过设置两个名为currentMIn和currentMax的计算变量为数组的前两个整型值。然后函数遍历数组中余下的值并检查每个值看他们是否比currentMin和currentMax代表的值小或者大。最后,全部最小和最大的值作为两个Int值的元祖返回。
因为元祖的成员值作为函数返回类型的一部分命名,他们可以使用点语法访问来获取发现的最小的和最大的值:
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"注意元祖的成员不需要在元祖从函数中返回的时候命名,因为它们的名字已经作为函数返回类型的一部分指定了。
可选元祖返回类型(Optional Tuple Return Types)
如果从函数中返回的元祖的类型有可能对整个元祖来说没有值,可以使用可选元祖返回类型来反映整个元祖可能为nil的事实。通过在元祖闭括号后面加一个问好来写一个可选的元祖返回类型,例如(Int,Int)?或者(String,Int,Bool)?。
像(Int,Int)?的可选元组类型和像(Int?,Int?)的包含可选类型的元祖不一样。可选元祖类型,整个元祖是可选的,不只是元祖中每一个单独的值。
上面的函数minMax(array:)返回了一个包含两个Int值的元祖。不过,函数没有对传进来的数组进行任何安全检查。如果数组参数包含了一个空数组,函数minMax(array:),上面定义的一样,在尝试访问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..<array.count] {
if value < currentMin {
currentMin = value
} else if value > 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"隐式返回的函数(Functions With an Implicit Return)
如果函数的整个主体是一个单一的表达式,函数默认返回那个表达式。例如,下面两个函数表现一样:
func greeting(for person: String) -> String {
"Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// Prints "Hello, Dave!"
func anotherGreeting(for person: String) -> String {
return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// Prints "Hello, Dave!"greeting(for:)函数的整个定义是他返回的greeting 信息,意味着他可以使用简便形式。anotherGreeting(for:)函数返回相同的greeting信息,像一个较长的函数使用return关键词。任何只写了return行的函数可以忽略return。
像你将要在Shorthand Getter Declaration中看到的,属性也可以默认返回
函数形参标签和实参名(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)全部的实参必需有独一无二的名称。即使可能多个实参有一样的形参标签,独一无二的形参标签帮你使代码可读性更好。
指定形参标签(Specifying Argument Labels)
在实参之前写一个形参标签,使用逗号隔开:
func someFunction(argumentLabel parameterName: Int) {
// In the function body, parameterName refers to the argument value
// for that parameter.
}这里是一个使用一个人的名字和城镇然后返回一个greeting的函数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."形参标签的使用可以使函数以一种富有表达性,像句子一样的方式调用,同时仍然提供可读性和明确目标的函数主体。
忽略形参标签(Omitting Argument Labels)
如果你不想要给实参一个形参,对那个实参使用下划线代替一个明确的形参标签。
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)如果一个实参有形参标签,当你调用函数时形参必须是标签的。
默认参数值(Default Parameter Values)
你可以在实参类型后面通过给实参分配一个值来为函数中任何实参定义一个默认的值。如果定义了默认的值,当调用函数时你可以忽略那个参数。
unc 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将没有默认值的实参放在函数实参列表的前面,在有默认值的实参前面。没有默认值的实参通常对函数的意义更重要--先写他们更容易识别调用的同样的函数,不管是否忽略了任何实参。
可变参数(Variadic Parameters)
一个可变实参接受另个或者多个指定类型的值。你用可变参数指定当函数调用时可以传递一个多种个数值的实参。通过在实参类型名字后面插入三个点(...)写可变参数。
传给可变参数的值在函数体内作为对应类型的数组获取。例如名字是numbers的一个可变参数和一个Double...类型在函数体内作为一个类型为[Double]名为numbers的常量数组获取。
下面的例子为任何长度的数字列表计算算术平均值:
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一个函数最多含有一个可变参数。
In-Out实参(In-Out Parameters)
函数实参默认是常量。试图从函数体内改变函数实参的值会导致编译时错误。这意味着你不能错误地改变实参的值。如果你想用函数修改一个实参的值,你想在函数调用结束后这些改变保留,将那些参数定义为in-out参数。
通过在实参类型前加inout关键字写in-out实参。一个in-out实参有一个传进函数的值,被函数修改,然后传出函数替代原来的值。关于探讨in-out参数表现的细节和相关地编译优化,查看In-Out Parameters。
对一个in-out参数你只能传一个变量作为参数。不能传一个常量或者字面量作为参数,因为常量和字面量不能修改。当你将它作为参数传给in-out参数时在变量名前面加一个取值符号(&),指明它可以被函数修改。
in-out参数不能有默认值,并且可变参数不能标记inout。
这里是一个名为swapTowInts(_:_:)的函数的例子,有两个叫a和b的in-out整型参数:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}函数swapTwoInts(_:_:)只是互换了b和a的值。函数通过存储a的值在一个名为temporaryA的临时常量里,将b的值分配各a,然后temporaryA的值给b来实现这个交换。
你可以用两个Int类型的变量来调用函数swapTwoInts(_:_:)来交换他们的值。注意当它们传给函数swapTwoInts(_:_:)时someInt和anotherInt的名字用一个ampersand(&)做前缀:
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"上面的例子展示了someInt和anotherInt的原始值通过函数swapTwoInts(_:_:)修改,即使他们原先在函数之外定义的。
in-out参数和从函数中返回一个值不同。上面swatpTwoInts的例子没有定义一个返回类型或者返回值,但是他仍然修改了someInt和anotherInt的值。In-out参数是函数在它的函数体之外产生影响的另一种方式。
函数类型(Function Type)
每一个函数有一个特殊的函数类型,由函数的实参类型和返回类型组成。
例如:
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的函数“。
使用函数类型(Using Function Types)
像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函数类型作为参数类型(Function Types as Parameter Types)
你可以使用像(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"这个例子定义了一个名为printMathResule(_:_:_:)函数,有三个参数。第一个参数名为mathFunction,类型为(Int,Int)->Int。你可以为这第一个参数传递一个那个类型的任意函数作为参数。第二个和第三个参数名为a和b,两个类型都是Int。他们为提供的数学函数用来做两个输入值。
当printMathResult(_:_:_:)调用的时候,他被传了函数addTwoInts(_:_:),和整型值3和5.它用3和5调用提供的函数,并打印结果8.
printMathResult(_:_:_:)的任务是打印得用数学函数的合适类型的结果。函数的实现具体做了什么不重要--重要的值是函数是正确的类型。这使printMathResult(_:_:_:)可以用一种类型安全的方式将他的一些功能转交给函数的调用者。
函数类型作为返回类型(Function Type as Return Types)
你可以用一个函数类型作为另一个函数的返回类型。通过在返回函数的返回箭头(->)后面立即写一个完成整的函数类型来做这件事。
下一个例子定义了两个名为stepForward(_:)和stepBackward(_:)的两个简单的函数。函数stepForward(_:)返回一个比他的输入值大一的值,函数stepBackward(_:)返回一个比他的输入值小一的值。两个函数有一个类型(Int)->(Int):
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}这里是一个名为chooseStepFunction(backward:)的函数,它的返回类型是(Int)->(Int)。函数chooseStepFunction(backward:)在一个名为backward的布尔值的基础上返回函数stepForward(_:)或者函数stepBackward(_:):
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}你现在可以使用chooseStepFunction(backward:)来获取一个将要在一个方向或者另外的方向前进的函数:
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function上面的例子决定了需要正数还是负数的步子来使一个名为currentValue的变量来渐渐地靠近0。currentValue有一个初始值3,一位置currenValue>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!内嵌函数(Nested Functions)
在这个章节中你用到的全部的函数已经用全局函数举例了,定义在全局区域。你也可以在其他函数体中定义函数,即内嵌函数。
内嵌函数默认从外部环境隐藏,但仍可以被他们的封闭函数调用和使用。一个粉笔函数可以返回它内嵌函数之一来让内嵌函数在其他区域使用。
你可以重写上面的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!