闭包

闭包

回顾嵌套函数

一门计算语言要支持闭包的前提有两个。

  • 支持函数类型,能够将函数作为参数或返回值传递
  • 支持函数嵌套
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)。即便是定义这些常量和变量的原始作用域已经不存在,嵌套函数或闭包仍然可以在函数体内或闭包体内引用和修改这些值。

你可能感兴趣的:(闭包)