函数
函数
函数定义
当你定义一个函数时,你可以选择性地定义一个或多个名称,类型值作为函数的输入
func sayHello(personName: String) -> String {
let greeting = "Hello, " + personName + "!"
return greeting
}
println(sayHello("Anna"))
// prints "Hello, Anna!"
println(sayHello("Brian"))
// prints "Hello, Brian!"
函数返回值
由于
无返回值函数
函数不需要定义一个返回类型。这里有一个版本的
func sayGoodbye(personName: String) {
println("Goodbye, \(personName)!")
}
sayGoodbye("Dave") // prints "Goodbye, Dave!"
严格地说,
单返回值函数
两个参数一个返回值,都为
func thirdFunction(firstValue: Int, secondValue: Int) -> Int {
return firstValue + secondValue
}
thirdFunction(1, 2)
多返回值函数
可以使用一个元组类型作为函数的返回类型,来返回一个由多个值组成的复合返回值。下面的例子定义了一个名为
func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {
var vowels = 0, consonants = 0, others = 0
for character in string {
switch String(character).lowercaseString {
case "a", "e", "i", "o", "u":
++vowels
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
++consonants
default:
++others
}
}
return (vowels, consonants, others)
}
上文中关于元组部分讲解时也提及,可以直接利用下标或者属性名来访问元组中值,因此,多返回值也可以这么使用:
func fourthFunction(firstValue: Int, secondValue: Int)
-> (doubled: Int, quadrupled: Int) {
return (firstValue * 2, secondValue * 4)
}
fourthFunction(2, 4)
// 用数字访问:
fourthFunction(2, 4).1 // = 16
// 其他相同,只是使用了名字:
fourthFunction(2, 4).quadrupled // = 16
函数变量
var numbersFunc: (Int, Int) -> Int;
// numbersFunc现在可以存储任何接受两个Int并返回一个Int的函数
numbersFunc = addNumbers
numbersFunc(2, 3) // = 5
参数调用
本地形参与外部形参
注意,全局函数中不可以使用外部形参
所有上面的函数都为其形参定义了形参名:
func someFunction(parameterName: Int) {
// function body goes here, and can use parameterName
// to refer to the argument value for that parameter
}
然而,这些参数名的仅能在函数本身的主体内使用,不能在调用函数时使用。这种形参类型名称被称之为本地形参名
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
func join(string s1: String, toString s2: String, withJoiner joiner: String)
-> String {
return s1 + joiner + s2
}
join(string: "hello", toString: "world", withJoiner: ", ")
// returns "hello, world"
外部参数名称速记#:2.0 之后已经废弃
在
Swift gives the first parameter name in a method a local parameter name by default, and gives the second and subsequent parameter names both local and external parameter names by default.
如果你想为一个函数提供一个外部形参名,然而本地形参名已经使用了一个合适的名称了,那你就不需要两次书写该形参的名称。相反,你可以写一次名字,并用一个
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}
该函数对形参名的选择使得其函数主题更加清晰易读,并且在调用该函数时也不会有歧义:
let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v"
使用_ 来避免使用下标
func foo(s1: String, _ s2: String) -> String{
return s1 + s2;
}
在正常的调用
bar.foo("Hello", "World")
不过需要注意的是,在
//不使用_
class IntClass {
var value: Int
init(val: Int) { self.value = val }
}
var x = IntClass(val:1)
//使用_
class IntClass {
var value: Int
init(_ val: Int) { self.value = val }
}
var x = IntClass(1)
默认形参值
你可以为任何形参定义默认值以作为函数定义的一部分。如果已经定义了默认值,那么调用函数时就可以省略该行参。
注意:请在函数形参列表的末尾放置带默认值的形参。这将确保所有函数调用都使用顺序相同的无默认值实参,并让在每种情况下清晰地调用相同的函数。
这里有一个早期的join函数,并为参数joiner设置了默认值:
func join(string s1: String, toString s2: String,
withJoiner joiner: String = " ") -> String {
return s1 + joiner + s2
}
如果在join函数调用时为joiner提供了字符串值,那么该字符串值可以用来连接两个字符串,就跟以前一样:
join(string: "hello", toString: "world", withJoiner: "-")
// returns "hello-world"
但是,如果函数调用时没有为joiner提供值,就会使用单个空格(" ")的默认值:
join(string: "hello", toString: "world")
// returns "hello world"
不定形参
一个可变形参可接受零个或多个指定类型的值。当函数被调用时,你可以使用可变形参来指定--形参可以用来传递任意数量的输入值。可通过在形参的类型名后边插入三个点符号(...)来编写可变形参。传递至可变形参的值在函数主体内是以适当类型的数组存在的。例如,一个可变参数的名称为numbers和类型为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, 19)
// returns 10.0, which is the arithmetic mean of these three numbers
常量形参和变量形参
**函数的形参默认是常量**。试图在函数体内改变函数形参的值会引发一个编译时错误。这意味着你不能错误地改变形参的值。但是有时候,函数有一个形参值的变量副本是非常有用的。您可以指定一个或多个形参作为变量形参,从而避免在函数内部为自己定义一个新的变量。变量参数是变量而非常量,并给函数一个可修改的形参值副本。
func alignRight(var string: String, count: Int, pad: Character) -> String {
let amountToPad = count - countElements(string)
for _ in 1...amountToPad {
string = pad + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, 10, "-")
// paddedString is equal to "-----hello"
// originalString is still equal to "hello"
In-Out 形参
如上描述,变量形参只能在函数本身内改变。如果你想让函数改变形参值,并想要在函数调用结束后保持形参值的改变,那你可以把形参定义为
提示:
in-out 参数不能有默认值,可变参数的参数也不能被标记为inout 。如果您标记参数为inout ,它不能同时被标记为var 或let 。
func swapTwoInts(inout a: Int, inout b: Int) {
let temporaryA = a
a = b
b = temporaryA
}
swapTwoInts函数只是简单地交换a、b的值。该函数通过存储一个名为temporaryA临时常量的值,指定b的值到a,然后分配temporaryA到b执行该交换。你可以通过两个Int类型的变量调用swapTwoInts函数,从而交换它们的值。需要注意的是当它们被传递给swapTwoInts函数时,someInt和anotherInt名称前要加上前缀符号&:
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"
函数类型
闭包
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。
闭包表达式
闭包表达式的语法有如下形式:
{ (parameters) -> returnType in
statements
}
闭包表达式语法可以使用常量、变量和
reversed = sort(names, { (s1: String, s2: String) -> Bool in
return s1 > s2
})
需要注意的是内联闭包参数和返回值类型声明与
根据上下文推断类型
因为排序闭包是作为函数的参数进行传入的,
reversed = sort(names, { s1, s2 in return s1 > s2 } )
单行表达式闭包可以省略return
单行表达式闭包可以通过隐藏
reversed = sort(names, { s1, s2 in s1 > s2 } )
参数名简写
reversed = sort(names, { $0 > $1 } )
Trailing 闭包
如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用
func someFunctionThatTakesAClosure(closure: () -> ()) {
// 函数体部分
}
// 以下是不使用 trailing 闭包进行函数调用
someFunctionThatTakesAClosure({
// 闭包主体部分
})
// 以下是使用 trailing 闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
在上例中作为
reversed = sort(names) { $0 > $1 }
捕获(Capture)
闭包可以在其定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
incrementor 函数并没有获取任何参数,但是在函数体内访问了 runningTotal 和 amount 变量。这是因为其通过捕获在包含它的函数体内已经存在的runningTotal 和 amount 变量而实现。
由于没有修改 amount 变量,incrementor 实际上捕获并存储了该变量的一个副本,而该副本随着 incrementor 一同被存储。然而,因为每次调用该函数的时候都会修改 runningTotal 的值,incrementor 捕获了当前 runningTotal 变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当 makeIncrementor 结束时候并不会消失,也保证了当下一次执行 incrementor 函数时,runningTotal 可以继续增加。
下面为一个使用
let incrementByTen = makeIncrementor(forIncrement: 10)
该例子定义了一个叫做 incrementByTen 的常量,该常量指向一个每次调用会加10的 incrementor 函数。调用这个函数多次可以得到以下结果:
incrementByTen() // 返回的值为10
incrementByTen() // 返回的值为20
incrementByTen() // 返回的值为30
如果您创建了另一个
let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven() // 返回的值为7
incrementByTen() // 返回的值为40