继续学习Swift文档,从上一章节:控制流,咱们学习了Swift控制流相关的内容,如for-in循环、while循环、if语句、switch语句、continue、break、fallthrough、带标签的语句、guard语句这些内容。其中switch语句在swift里的用法与OC相比,有很大的差异,功能更丰富,具体的能够参阅上一章节的内容。如今,咱们学习Swift的函数相关的内容。这一章节的内容很重要了,几乎全部开发语言都离不开函数的使用,因此咱们要认真对待并熟练掌握这一章节的内容。因为篇幅较长,这里分篇来记录,接下来,Fighting!html
若是你已经熟练掌握了函数的使用,那么请参阅下一章节:闭包swift
函数是执行特定任务的独立代码块。 您为函数指定一个名称,该名称能够标识其功能,而且该名称用于“调用”该函数以在须要时执行其任务。数组
Swift的统一函数语法足够灵活,能够表达任何内容,从没有参数名称的简单C样式函数到具备每一个参数名称和参数标签的复杂的Objective-C样式方法。 参数能够提供默认值以简化函数调用,而且能够做为输入-输出参数传递,参数在函数完成执行后会修改传递的变量。安全
Swift中的每一个函数都有一个类型,包括该函数的参数类型和返回类型。 您能够像Swift中的任何其余类型同样使用此类型,这使得将函数做为参数传递给其余函数以及从函数返回函数变得容易。 也能够在其余函数中编写函数,以将有用的函数封装在嵌套函数范围内。bash
定义函数时,能够选择定义一个或多个该函数做为输入的命名,键入的值,称为参数。 您还能够选择定义一种值类型,该函数完成后将做为输出传回,称为函数的返回类型。闭包
每一个功能都有一个功能名称,该名称描述了该功能执行的任务。 要使用函数,请使用函数名称“调用”该函数,而后将与函数参数类型匹配的输入值(称为自变量)传递给它。 必须始终以与函数参数列表相同的顺序提供函数的参数。ide
在下面的示例中,该函数称为greet(person :),由于它就是这样作的,它将一我的的名字做为输入并返回该人的问候语。 为此,您须要定义一个输入参数-一个名为person的String值,以及一个String返回类型,其中将包含对该人的问候:函数
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
复制代码
全部这些信息都会汇总到函数的定义中,并以func关键字为前缀。 您可使用返回箭头->(连字符后接直角括号)指示函数的返回类型,其后是要返回的类型的名称。post
定义描述了函数的功能,指望接收的功能以及完成后返回的内容。 经过定义,能够轻松地从代码中的其余地方明确调用该函数:学习
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 :)函数的调用中,以打印该字符串并查看其返回值,如上所示。
注意
print(_:separator:terminator :)函数的第一个参数没有标签,其余参数是可选的,由于它们具备默认值。 有关函数语法的这些变体,将在下面的“函数参数标签”,“参数名称”和“默认参数值”中进行讨论。
greet(person :)函数的主体经过定义一个新的String常量(称为greeting)并将其设置为简单的问候消息开始。 而后,使用return关键字将此greeting传回函数。 在表示返回greeting的代码行中,该函数完成其执行并返回greeting的当前值。
您可使用不一样的输入值屡次调用greet(person :)函数。 上面的示例显示了若是使用输入值“ Anna”和输入值“ Brian”进行调用会发生的状况。 该函数在每种状况下都会返回定制的问候语。
为了使该函数的主体更短,能够将消息建立和return语句合并为一行:
func greetAgain(person: String) -> String {
return "Hello again, " + person + "!"
}
print(greetAgain(person: "Anna"))
// Prints "Hello again, Anna!"
复制代码
函数参数和返回值在Swift中很是灵活。 您能够定义任何东西,从具备单个未命名参数的简单实用程序函数到具备表达性参数名称和不一样参数选项的复杂函数。
不须要功能来定义输入参数。 这是一个没有输入参数的函数,不管什么时候调用,该函数老是返回相同的String消息:
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world"
复制代码
尽管函数定义没有任何参数,但仍须要在函数名称后加上括号。 调用函数时,函数名称后还会有一对空括号。
函数能够具备多个输入参数,这些参数写在函数的括号内,并用逗号分隔。
此函数接受一我的的名字以及是否已经打过招呼,而后返回该人的问候语:
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参数值和标为hasgreeted的Bool参数值都传递给括号来调用greet(person:alreadyGreeted :)函数,并用逗号分隔。 请注意,此函数不一样于前面部分中显示的greet(person :)函数。 尽管两个函数的名称都以greet开头,可是greet(person:alreadyGreeted :)函数采用两个参数,而greet(person :)函数仅采用一个参数。
不须要函数来定义返回类型。 这是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 :)打印一个字符串,而后将其字符计数做为Int返回。 第二个函数printWithoutCounting(string :)调用第一个函数,但忽略其返回值。 当调用第二个函数时,消息仍然由第一个函数打印,可是不使用返回的值。
注意
返回值能够忽略,可是说返回值的函数必须始终这样作。 在函数的底部结束时,具备定义了返回类型的函数不返回值,会致使编译时错误。
您可使用元组类型做为函数的返回类型,以将多个值做为一个复合返回值的一部分返回。
下面的示例定义了一个名为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"
复制代码
请注意,在从函数返回元组时不须要命名元组的成员,由于它们的名称已被指定为函数返回类型的一部分。
若是要函数返回的元组类型,多是“无值”,则可使用可选的元组返回类型来反映整个元组能够为零。 您经过在元组类型的右括号后面加上问号来编写可选的元组返回类型,例如(Int,Int)? 或(字符串,整数,布尔)?
注意
可选的元组类型,例如(Int,Int)? 与包含可选类型(例如(Int ?, Int?))的元组不一样。 对于可选的元组类型,整个元组是可选的,而不只仅是元组中的每一个单独的值。
上面的minMax(array :)函数返回一个包含两个Int值的元组。 可是,该函数不会对传递的数组执行任何安全检查。 若是array参数包含一个空数组,则如上所述,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"
复制代码
若是函数的整个主体是单个表达式,则函数隐式返回该表达式。 例如,下面的两个函数具备相同的行为:
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 :)函数定义的是它返回的问候消息,这意味着它可使用这种较短的形式。 anotherGreeting(for :)函数使用相似的函数的return关键字返回相同的问候消息。 您只写一个返回行的任何函数均可以忽略该返回。
就像在Shorthand Getter Declaration中所看到的那样,属性getter也可使用隐式返回。
每一个函数参数都具备参数标签和参数名称。 参数标签在调用函数时使用; 每一个参数都写在函数调用中,并带有其参数标签。 参数名称在函数的实现中使用。 默认状况下,参数使用其参数名称做为其参数标签。
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)
复制代码
全部参数必须具备惟一的名称。 尽管多个参数可能具备相同的参数标签,但独特的参数标签有助于使代码更具可读性。
您在参数名称前写一个参数标签,并用空格隔开:
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."
复制代码
变量标签的使用可让函数以表达方式,相似于句子的方式被调用,同时还能够提供可读且意图明确的函数主体。
若是您不但愿为参数添加参数标签,请为该参数写下划线(_)而不是显式参数标签。
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)
复制代码
若是参数具备参数标签,则在调用函数时必须对参数进行标签。
您能够为函数中的任何参数定义默认值,方法是在该参数的类型以后为该参数分配一个值。 若是定义了默认值,则能够在调用函数时忽略该参数。
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
复制代码
将没有默认值的参数放在函数的参数列表的开头,在具备默认值的参数以前。 没有默认值的参数一般对函数的意义更重要-首先编写它们可使您更容易识别正在调用相同的函数,而不论是否忽略了任何默认参数。
可变参数接受零个或多个指定类型的值。 您可使用可变参数来指定在调用函数时能够向该参数传递不一样数量的输入值。 经过在参数的类型名称后插入三个句点(...)来编写可变参数。
传递给可变参数的值可在函数体内以适当类型的数组形式使用。 例如,在函数体内可使用带有数字名称和Double ...类型的可变参数做为称为Numbers [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
复制代码
注意
一个函数最多能够具备一个可变参数。
函数参数默认为常量。 试图从函数主体内部更改函数参数的值会致使编译时错误。 这意味着您不能错误地更改参数的值。 若是您但愿函数修改参数的值,而且但愿这些更改在函数调用结束后仍然存在,请将该参数定义为in-out参数。
您能够经过将in-out关键字放在参数类型的前面来编写in-out参数。 in-out参数具备一个值,该值传递给函数,由函数修改,而后传递回函数以替换原始值。 有关in-out参数的行为以及相关的编译器优化的详细讨论,请参见In-Out Parameters。
您只能将变量做为in-out参数的参数传递。 您不能将常量或文字值做为参数传递,由于没法修改常量和文字。 当您将"与"符号(&)用做变量的参数时,能够直接在其名称前放置一个&符号,以代表该变量能够被函数修改。
注意
in-out 参数不能有默认值,而且可变参数不能被用为in-out参数
这是一个名为swapTwoInts(: :)的函数的示例,该函数具备两个称为a和b的in-out整数参数:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
复制代码
swapTwoInts(: :)函数只是将b的值交换为a,而后将a的值交换为b。 该函数经过将a的值存储在一个称为临时A的临时常量中,而后将b的值赋给a,而后将临时A赋给b来执行此交换。
您可使用两个Int类型的变量调用swapTwoInts(: :)函数来交换它们的值。 请注意,当将someInt和anotherInt的名称传递给swapTwoInts(: :)函数时,它们的前缀为&符:
ar 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参数与从函数返回值不一样。 上面的swapTwoInts示例未定义返回类型或返回值,但仍修改someInt和anotherInt的值。 in0out参数是函数在函数主体范围以外产生影响的另外一种方法。
每一个函数都有特定的函数类型,由函数的参数类型和返回类型组成。
例如:
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
复制代码
此示例定义了两个简单的数学函数,分别称为addTwoInts和multipleTwoInts。 这些函数各自采用两个Int值,并返回一个Int值,这是执行适当的数学运算的结果。
这两个函数的类型都是(Int,Int)-> Int。 能够理解为:
“具备两个参数(均为Int类型)而且返回值为Int类型的值的函数。”
这是另外一个示例,针对没有参数或返回值的函数:
func printHelloWorld() {
print("hello, world")
}
复制代码
此函数的类型为()-> Void,或“没有参数并返回Void的函数”。
您能够像使用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
复制代码
您可使用诸如(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"
复制代码
本示例定义了一个名为printMathResult(::_ :)的函数,该函数具备三个参数。 第一个参数称为mathFunction,类型为(Int,Int)-> Int。 您能够将该类型的任何函数做为第一个参数的参数传递。 第二个和第三个参数分别称为a和b,而且均为Int类型。 这些用做提供的数学函数的两个输入值。
调用printMathResult(::_ :)时,将传递addTwoInts(: :)函数以及整数值3和5。它将调用提供的函数,其值分别为3和5,并打印结果 8。
printMathResult(::_ :)的做用是打印对适当类型的数学函数的调用结果。 该函数的实现实际执行什么可有可无,仅与该函数的类型正确无关。 这使printMathResult(::_ :)以类型安全的方式将其某些功能移交给函数的调用者。
您能够将一个函数类型用做另外一个函数的返回类型。 您能够经过在返回函数的返回箭头(->)以后当即编写完整的函数类型来执行此操做。
下一个示例定义了两个简单的函数,分别称为stepForward(_ :)和stepBackward(_ :)。 stepForward(_ :)函数返回的值比其输入值大一,而stepBackward(_ :)函数返回的值比其输入值小一。 这两个函数的类型都是(Int)-> Int:
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
复制代码
这是一个名为choiceStepFunction(backward :)的函数,其返回类型为(Int)-> Int。 choiceStepFunction(backward :)函数基于布尔值向后返回stepForward(_ :)函数或stepBackward(_ :)函数:
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
复制代码
如今,您可使用choiceStepFunction(backward :)来得到向一个方向或另外一个方向步进的函数:
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function
复制代码
上面的示例肯定是否须要正步或负步来逐步将名为currentValue的变量逐渐移近零。 currentValue的初始值为3,这意味着currentValue> 0返回true,从而致使selectStepFunction(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!
复制代码
到目前为止,您在本章中遇到的全部功能都是全局函数的示例,这些函数是在全局范围内定义的。 您还能够在其余函数体内定义函数,称为嵌套函数。
嵌套函数默认状况下对外界隐藏,但仍能够由其封闭函数调用和使用。 封闭函数还能够返回其嵌套函数之一,以容许该嵌套函数在另外一个做用域中使用。
您能够重写上面的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!
复制代码
这一章节内容到这里就结束了,咱们作个小结:
func someFunction(_ firstParameterName: Int, secondParameterName: Int)
,调用时就能够someFunction(1, secondParameterName: 2)
。func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12)
,调用时:someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6)
或someFunction(parameterWithoutDefault: 4)
。func arithmeticMean(_ numbers: Double...) -> Double
,调用时:arithmeticMean(1, 2, 3, 4, 5)
。func swapTwoInts(_ a: inout Int, _ b: inout Int)
,调用时,要声明两个var变量做为参数,并传入函数:swapTwoInts(&someInt, &anotherInt)
,用“&+变量”做为参数。最后一句话,喜欢的朋友能够给个👍吗,你的鼓励就是个人动力,嘿嘿~
参考文档:Swift - Functions