闭包
回顾嵌套函数
一门计算语言要支持闭包的前提有两个。
- 支持函数类型,能够将函数作为参数或返回值传递
- 支持函数嵌套
print("--嵌套函数--")
func calculate(opr:String) -> (Int, Int) -> Int {
//定义 + 函数
func add(a: Int, b: Int) -> Int {
return a + b
}
//定义 - 函数
func sub(a: Int, b: Int) -> Int {
return a - b
}
var result : (Int, Int) -> Int
switch (opr) {
case "+":
result = add
case "-":
result = sub
default:
result = add
}
return result
}
let f1:(Int, Int) -> Int = calculate("+")
let f2:(Int, Int) -> Int = calculate("-")
print("10 + 5 = \(f1(10, 5))")
print("10 - 5 = \(f2(10, 5))")
闭包的概念
print("---闭包---")
func calculate_(opr: String) -> (Int, Int) -> Int {
var result : (Int, Int) -> Int
switch (opr) {
case "+":
result = {(a: Int, b: Int) -> Int in
return a + b
}
default:
result = {(a: Int, b: Int) -> Int in
return a - b
}
}
return result
}
let f11:(Int, Int) -> Int = calculate("+")
print("10 + 5 = \(f11(10, 5))")
let f22:(Int, Int) -> Int = calculate("-")
print("10 - 5 = \(f22(10, 5))")
原来的嵌套函数add和sub被替代,整理出来就是如下两种形式:
{(a: Int, b: Int) -> Int in //替代函数add
return a + b
}
{(a: Int, b: Int) -> Int in //替代函数sub
return a - b
}
还可以写成一行,如下所示:
{(a: Int, b: Int) -> Int in return a + b }
{(a: Int, b: Int) -> Int in return a - b }
Swift中的闭包定义:闭包是自包含的匿名函数代码块,可以作为表达式、函数参数和函数返回值,闭包表达式的运算结果是一种函数类型。
Swift中的闭包类似于C和Objective-C中的代码块、C++和C#中的Lambda表达式、Java中的匿名内部类。
Swift中的闭包可以捕获和存储其所在上下文环境中的常量和变量。这种引用事实上会引起比较麻烦的内存管理问题,好在Swift不需要程序员管理内存。
使用闭包表达式
Swift中的闭包表达式很灵活,其标准语法格式如下:
{ (参数列表) -> 返回值类型 in
语句组
}
其中,参数列表与函数中的参数列表形式一样,返回值类型类似于函数中的返回值类型,但不同的是后面有in关键字。
Swift提供了多种闭包简化写法。
类型推断简化
类型推断简化是Swift的强项,Swift可以根据上下文环境推断出参数类型和返回值类型。
标准形式的闭包:
{ (a: Int, b: Int) -> Int in
return a + b
}
Swift能推断出参数a和b是Int类型,返回值也是Int类型。简化形式如下:
{a, b in return a + b}
使用这种简化方式修改后的示例代码如下:
print("---类型推断简化---")
func calculate__(opr :String)-> (Int,Int)-> Int {
var result : (Int,Int)-> Int
switch (opr) {
case "+" :
result = {a, b in return a + b }
default:
result = {a, b in return a - b }
}
return result
}
let c1:(Int,Int)-> Int = calculate__("+")
print("10 + 5 = \(c1(10,5))")
let c2:(Int,Int)-> Int = calculate__("-")
print("10 - 5 = \(c2(10,5))")
隐藏return关键字
如果在闭包内部语句组只有一条语句,如return a + b等,那么这种语句都是返回语句。前面的关键字return可以省略,省略形式如下:
{a, b in a + b}
使用这种简化方式修改后的示例代码如下:
print("--隐藏return关键字--")
func calculate____(opr:String) -> (Int, Int) -> Int {
var result : (Int, Int) -> Int
switch (opr) {
case "+":
result = {a, b in a + b}
default:
result = {a, b in a - b}
}
return result
}
上述代码中的闭包将return关键字省略了,需要注意的是,省略的前提是闭包中只有一条return语句。
缩写参数名称
Swift提供了参数名称缩写功能,可以用$0、$1、$2来表示调用闭包中参数,$0指代第一个参数,$1指代第二个参数,$2指代第三个参数,以此类推$n + 1指代第n个参数。
使用参数名称缩写,还可以在闭包中省略参数列表的定义,Swift能够推断出这些缩写参数的类型。此外,in关键字也可以省略。参数名称缩写之后如下表示:
{$0 + $1}
使用参数名称缩写修改后的示例代码如下:
print("---缩写参数名称---")
func calculate_____(opr : String) -> (Int, Int) -> Int {
var result : (Int, Int) -> Int
switch (opr) {
case "+":
result = {$0 + $1}
default:
result = {$0 - $1}
}
return result
}
let g1:(Int, Int) -> Int = calculate_____("+")
print("10 + 5 = \(g1(10, 5))")
let g2:(Int, Int) -> Int = calculate_____("-")
print("10 - 5 = \(g2(10, 5))")
使用闭包返回值
闭包表达本质上是函数类型,是有返回值的,可以直接在表达式中使用闭包的返回值。
print("---使用闭包返回值----")
let cc1:Int = {(a: Int, b: Int) -> Int in
return a + b
} (10, 5)
print("10 + 5 = \(cc1)")
let cc2: Int = {(a : Int, b: Int) -> Int in
return a - b
}(10, 5)
print("10 - 5 = \(cc2)")
第一行代码是给cc1赋值,后面是一个闭包表达式,但是闭包表达式不能直接赋值给cc1,因为cc1是Int类型,需要闭包的返回值。在闭包结尾的大括号后面接一对小括号(10, 5),通过小括号(10,5)为闭包传递参数。通过这种方法可以为变量和常量直接赋值,在有些场景下使用非常方便。
使用尾随闭包
闭包表达式可以作为函数的参数传递,如果闭包表达式很长,就会影响程序的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
print("---使用尾随闭包---")
func calculate1(opr:String, funN:(Int, Int) -> Int) {
switch (opr) {
case "+":
print("10 + 5 = \(funN(10, 5))")
default:
print("10 - 5 = \(funN(10, 5))")
}
}
calculate1("+", funN: {(a: Int, b:Int) -> Int in return a + b})
calculate1("-") {(a: Int, b: Int) -> Int in return a - b}
上述代码,其中最后一个参数funN是(Int, Int) -> Int函数类型,funN可以接收闭包表达式。
calculate1("+", funN: {(a: Int, b:Int) -> Int in return a + b})是调用过程,{(a: Int, b:Int) -> Int in return a + b}是传递的参数。这个参数很长,可以通过calculate1("-") {(a: Int, b: Int) -> Int in return a - b}这行代码调用,将闭包表达式移到()之外,这种形式就是尾随闭包。
需要注意的是,闭包必须是参数列表的最后一个参数,函数采用如下形式定义:
func calculate(funN:(Int, Int) -> Int, opr: String) {
...
}
如果闭包表达式不是最后一个,那么是不能使用尾随闭包写法的。
捕获上下文中的变量和常量
嵌套函数或闭包可以访问它所在上下文的变量和常量,这个过程称为捕获值(capturing value)。即便是定义这些常量和变量的原始作用域已经不存在,嵌套函数或闭包仍然可以在函数体内或闭包体内引用和修改这些值。