函数
语法定义
先来看下swift中函数的定义,函数用关键字func来指定,语法相对复杂一点,主要有下列4种基本情况,还有比较复杂的,会在后续详细讲解。
无参函数定义
有参函数定义
一个简单的函数和函数调用示例如下:
func printGreeting1() {print("Hello, playground.")}printGreeting1()func printGreeting2() ->String {return "Hello, playground.";}lef str = printGreeting2()
函数参数
函数有参数(parameter)之后就能做更多的事情了。利用参数可以向函数输入数据。我们之所以把函数的这部分称为“参数”,是因为它们可以根据调用者给函数传递的数据来改变自己的值。函数利用传递给自己的参数来执行任务或产生结果。 创建一个函数,利用参数打印更加个性化的问候信息,代码如下所示:
//带一个参数func printPersonalGreeting(name: String) {print("Hello \(name), welcome to your playground.")}printPersonalGreeting(name: "Matt")
参数内部名称
语法格式为:
paramName:ParamType
,前面是参数名称,后面指参数类型。
//带两个参数func divisionDescriptionFor(numerator: Double, denominator: Double) {print("\(numerator) divided by \(denominator) equals \(numerator / denominator)")}divisionDescriptionFor(numerator: 9.0, denominator: 3.0)
参数外部名称
语法格式为:
outParName inParName:ParamType
,依次表示外部调用时的参数名称,函数内部使用时的参数名称,参数类型。
这主要是为了代码的可读性更强而设计的,并没其它实际意义,但在调用时名称要匹配,如下例为外部调用重新声了一个名为to的参数名称以专让供外部调用使用。
func printPersonalGreeting(to name: String) { 13 print("Hello \(name), welcome to your playground.")
}
printPersonalGreeting(to: "Matt")
变长参数
语法格式为:
paramName:ParamType...
注意最后面的三个点,就是变长参数的特殊关键字。
函数只能有一个变长参数,而且一般应该是函数参数列表中的最后一个。参数值在函数内是以数组的形式使用,比如下例所示:
func printPersonalGreetings(to names: String...) {for name in names {print("Hello \(name), welcome to the playground.")}
}
printPersonalGreetings(to: "Alex","Chris","Drew","Pat")
参数默认值
语法格式为:
paramName:ParamType=value
用=号进行默认值赋值操作。
默认值应该放在函数参数数列表的末尾,如果形参有默认值,那么在调用函数时可以省略实参。
func divisionDescriptionFor(numerator: Double,denominator: Double, withPunctuation punctuation: String = ".") -> String { //默认值设置return "\(numerator) divided by \(denominator) equals \(numerator / denominator)\(punctuation)"
}//9.0 divided by 3.0 equals 3.0. 少传一个参数
print(divisionDescriptionFor(numerator: 9, denominator: 3))
//9.0 divided by 3.0 equals 3.0! 替换默认参数
print(divisionDescriptionFor(numerator: 9, denominator: 3, withPunctuation: "!"))
in-out 参数
语法格式为:
paramName:input ParamType
,这里的input是一个关键字。
函数有时候需要修改实参的值。in-out(in-out parameter)能让函数影响函数体以外的变量。有两个注意事项:
- 首先,in-out不能有默认值;
- 其次,变长参数不能标记为inout;
in-out的作用其实就是用于实参值的修改,省去return的写法,也可认为是达到多返回的目的。
var error = "The request failed:"
//_是一个外部函数名,它有特殊含义,即如果定义外部参数名为_,则在函数被调用时可以不写参数名称
func appendErrorCode(_ code: Int, toErrorString errorString: inout String) {if code == 400 {errorString += " bad request."}
}//用inout修饰时,调用时需要加上&,表示函数会修改这个变量,此段代码运行最后error的值会被改写成The request failed: bad request. 这有点像return干的事。
appendErrorCode(400, toErrorString: &error)
error
函数返回值
函数结束执行后可以返回一些信息,这些信息称为函数的返回值,用return关键字来标识。
单个返回值
一个简单的例子,如下
func divisionDescriptionFor() -> String {return "numerator"
}var v = divisionDescriptionFor();
多个返回值
语法结构 :
->(name1:[DataType], name2:[DataType])
,前面的name1和name2也可以省略,但不建议。
函数可以返回不止一个值。Swift用元组数据类型来做到这一点。
func sortEvenOddNumbers(_ numbers: [Int]) -> (evens: [Int], odds: [Int]) {var evens = [Int]()var odds = [Int]()for number in numbers {if number % 2 == 0 {evens.append(number)} else {odds.append(number)}}return (evens, odds)
}
let aBunchOfNumbers = [10,1,4,3,57,43,84,27,156,111]
let theSortedNumbers = sortEvenOddNumbers(aBunchOfNumbers)//The even numbers are: [10, 4, 84, 156]; the odd numbers are: [1, 3, 57, 43, 27, 111]
print("The even numbers are: \(theSortedNumbers.evens); the odd numbers are: \(theSortedNumbers.odds)")//一个更复杂的例子
func siftBeans(fromGroceryList list: [String]) -> (beanCount: Int, beansBought: [String])
多返回值运算
func +(lhs: Point, rhs: Point) -> Point {let newX = (lhs.x + rhs.x)let newY = (lhs.y + rhs.y)return Point(x: newX, y: newY)
}let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 3, y: 5)let p3 = p1 + p2 //(4,7)
p3.x
p3.y
可空类型返回
某些情况下希望函数可返回一个可空实例(用?或!来标识的返回值),主要用于一个函数在某些情况下返回nil,在其它情况下返回一个有意义的实例值的场景
//此函数只有一个参数,参数为元组类型,其中元组的第二个值可有可无的
func grabMiddleName(fromFullName name: (String, String?, String)) -> String? {return name.1 //元组的索引,表示取参数中元组的第二个值,索引从0开始
}//因为函数调用时第二个元组值为nil,所以不会打印任何信息
let middleName = grabMiddleName(fromFullName: ("Matt",nil,"Mathias"))
if let theName = middleName {print(theName)
}
函数返回与中断
主要是用return语句,但这里用了一个特殊的语法guard,熟悉下其用法就可以了。
func greetByMiddleName(name: (first: String, middle: String?, last: String)) {//把middle的值绑定到middleName常量上guard let middleName = name.middle else {print("Hey there!")return}print("Hey \(middleName)!")
}greetByMiddleName(name: ("Matt","Danger","Mathias"))
嵌套函数
Swift的函数定义可以嵌套。嵌套函数在另一个函数定义的内部声明并实现。嵌套函数在包围它的函数以外不可用。当需要一个函数只在另一个函数内部做一些事情时,这个特性很有用。
func areaOfTriangleWith(base: Double, height: Double) -> Double {let numerator = base * height//嵌套函数func divide() -> Double {return numerator / 2}return divide()
}
areaOfTriangleWith(base: 3.0, height: 5.0) //~~ 7.5
声明函数型变量
主要是把函数定义为一个普通变量,这样就可以把函数做为参数传递了,比如下面代码的实现,就是为了把sortEvenOddNumbers做为参数传递。
func sortEvenOddNumbers(_ numbers: [Int]) -> (evens: [Int], odds: [Int]) {return (evens, odds)
}//创建了一个evenOddFunction常量,其值是sortedEvenOddNumbers(_:)函数
let evenOddFunctionType: ([Int]) -> ([Int], [Int]) = sortEvenOddNumbers
闭包
闭包是在应用中完成特定任务的互相分离的功能组,类似于函数但省去了命名和声明,相比函数而言会比较轻量化。先举一个子来一个直观的感受,下面是一个数组排序的例子:
let volunteerCounts = [1,3,40,32,2,53,77,13]func sortAscending(_ i: Int, _ j: Int) -> Bool {return i < j
}
//~~ [1, 2, 3, 13, 32, 40, 53, 77]
let volunteersSorted = volunteerCounts.sorted(by: sortAscending)
如果采用闭包,则代码可以更简单,如下所示:
let volunteerCounts = [1,3,40,32,2,53,77,13]
let volunteersSorted = volunteerCounts.sorted(by: {(i: Int, j: Int) -> Bool inreturn i < j
})
语法结构
基础语法如下例:
{ (parameters) -> return type in // 代码
}
- 闭包整体代码写在 {} 中;
- (parameters) 是参数部分;
- -> return type 表示返回值
- in 是一个关键字,用来分隔前两部分与代码的实现;
上面的例子还可以再简化:
//简化版本1
let volunteersSorted = volunteerCounts.sorted(by: { i, j in i < j })//简化版本2:$0代表第一个参数,$1代表第二个参数,依此类推,称为快捷语法参数
let volunteersSorted = volunteerCounts.sorted { $0 < $1 }
闭包的写法虽然可以简单再简单,但需要和可读性之间做下权衡,个人还是比较建议用第1种写法,这也是大多数语言的实现方式。
闭包做为函数返回值
一般应用于函数的返回值为另一个函数的场景。下面的makeTownGrand函数返回值为一个函数,注意第一个->后面的(Int, Int) -> Int 其实是一个闭包的写法。
func makeTownGrand() -> (Int, Int) -> Int {func buildRoads(byAddingLights lights: Int, toExistingLights existingLights: Int) -> Int {return lights + existingLights}return buildRoads
}
var stoplights = 4
//此处townPlanByAddingLightsToExistingLights是一个函数变量
let townPlanByAddingLightsToExistingLights = makeTownGrand()
stoplights = townPlanByAddingLightsToExistingLights(4, stoplights)
print("Town has \(stoplights) stop lights.") //Town has 8 stop lights.
闭包做为函数参数
一般应用于函数的参数为另一个函数的场景。下成示例中makeTownGrand函数的第二个参数为一个函数。
func makeTownGrand(withBudget budget: Int, condition: (Int) -> Bool) //第二个参数为一个函数-> ( (Int, Int) -> Int )? { //返回值为函数if condition(budget) {func buildRoads(byAddingLights lights: Int, toExistingLights existingLights: Int) -> Int {return lights + existingLights}return buildRoads} else {return nil}
}//本例子中做为函数参数传统
func evaluate(budget: Int) -> Bool {return budget > 10_000
}var stoplights = 4
//evaluate函数为第二个参数
if let townPlanByAddingLightsToExistingLights = makeTownGrand(withBudget: 1_000, condition: evaluate) {stoplights = townPlanByAddingLightsToExistingLights(4, stoplights)
}if let newTownPlanByAddingLightsToExistingLights = makeTownGrand(withBudget: 10_500, condition: evaluate) {stoplights = newTownPlanByAddingLightsToExistingLights(4, stoplights)
}//~~ Town has 8 stop lights.
print("Town has \(stoplights) stop lights.")
捕获闭包函数内部的变量
闭包和函数能记录在其闭合作用域中定义的变量所封装的内部信息。下面的makePopulationTracker函数的返回值为一函数类型。
func makePopulationTracker(forInitialPopulation population: Int) -> (Int) -> Int {var totalPopulation = populationfunc populationTracker(growth: Int) -> Int {totalPopulation += growthreturn totalPopulation}return populationTracker
}var currentPopulation = 5_422//growBy为一函数
let growBy = makePopulationTracker(forInitialPopulation: currentPopulation) //返回函数(Int) -> Int
growBy(500) //5922
growBy(500) //6422
growBy(500) //6922
currentPopulation = growBy(500) //7422
再往下写点代码,前两行代码要说明的是闭包是引用类型,当把函数赋给一个常量或变量时,实际上是一个指针引用,并不是副本。但后面两行代码如果通过新的变量指向这个函数,则其作用域变会改变了。
let anotherGrowBy = growBy
anotherGrowBy(500) //7922var bigCityPopulation = 4_061_981
let bigCityGrowBy = makePopulationTracker(forInitialPopulation: bigCityPopulation) //7422bigCityPopulation = bigCityGrowBy(10_000) //4071981
lamda函数式编程
Swift的函数式编程和java差不太多。简单来讲lamda表达式,使代码更简洁,但也更难维护。闭包是实现函数编程的基础,因为函数式编程要求闭包函数可以和其它基本数据类型一样,可作为返回值从别的函数返回,也可以作为参数传递给别的函数,还可以存储在变量中。
map( _: )映射
遍历集合,实现原始数据的变换操作
let precinctPopulations = [1_244, 2_021, 2_157]
let projectedPopulations = precinctPopulations.map {(population: Int) -> Int inreturn population * 2
}
projectedPopulations //[2488, 4042, 4314]
filter( _: )筛选
遍历集合,实现原始数据的筛选
let precinctPopulations = [2488, 4042, 4314]
let bigProjections = projectedPopulations.filter {(projection: Int) -> Bool inreturn projection > 4_000
}
bigProjections //[4042, 4314]
reduce( _ : _ : )统计
遍历集合,实现原始数据的累加统计
let precinctPopulations = [2488, 4042, 4314]
let totalProjection = projectedPopulations.reduce(0) {(accumulatedProjection: Int, precinctProjection: Int) -> Int inreturn accumulatedProjection + precinctProjection
}
totalProjection //10844