概念
闭包是自包含的函数代码块,可以在代码中被传递和使用。闭包可以捕获和存储其所在上下文中任意常量和变量的引用。这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。
语法
1 | // 闭包的表达式 |
我们一起做几个例子来加深对闭包的了解。首先做个简单的加法+
,一般这么写:
1 | func adder(_ a: Double, _ b: Double) -> Double { |
其实两个数相加的目的已经达到,但我们还不满足它只能做加+
运算。希望还可以计算减-
、乘x
、除÷
运算。
1 | func calculate(_ op1: Double, _ op2: Double, symbol: String) -> Double { |
这段糟糕的代码看起还不错,但这真的是我们想要的么?美观?但不实用,简洁?却不优雅。因为它并不能明确告诉我们有几种计算方式,况且字符串与数学计算结合到一起也不是什么好的注意,我相信没有人会喜欢的,因此还需要继续优化它。
1 | func calculate(_ op1: Double, _ op2: Double, symbol: (Double, Double) -> Double) -> Double { |
看起来是好了很多,却增加了外部函数的依赖,比起之前的写法确实要好一些,但并不 Swift,我们可以把adder(_:_:)
方法的表达式写到调用者参数
中:
1 | // 如果是这样写的,编译器会很遗憾地告诉你这是错误的写法 |
第二种写法为什么正确?首先闭包表达式是一个匿名闭包,不能有函数名。所以我们需要去掉函数名将表达式用{}
括起来,还需要用in
关键字将参数、返回值与函数体分开便于编译器区分,而第二种写法正是这么做的。
Swift 的闭包非常神奇,可以有多种写法,这些写法都是有规律可寻的。
- 利用上下文推断参数和返回值类型
- 隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字
- 参数名称缩写
- 尾随(Trailing)闭包语法
1 | // 最后可以写成下面这样的, 其中的变换也不难 |
但是还有一种比上面更优雅的写法(23333)
1 | func calculate<T>(res: T) -> T { return res } |
思考
这道题是我从唐巧的技术博客中看到的。题目:我们需要构造一个工厂函数,这个函数接受两个函数作为参数,返回一个新的函数。新函数是两个函数参数的叠加作用效果。
举一个具体的例子,假如我们有一个 +2
的函数,有一个 +3
的函数,那用这个工厂函数,我们可以得到一个 +5
的函数。
又比如我们有一个 *2
的函数,有一个 *5
的函数,用这个工厂函数,我们就可以得到一个 *10
的函数。
1 | func funcBuild(_ f: @escaping (Int) -> Int, _ g: @escaping (Int) -> Int) -> (Int) -> Int { |
因为这里面只有最终的结果,并没有推导的过程,所以开始看的时候很吃力,并且有两个地方不太明白,函数的实现部分的闭包调用与函数调用时参数的闭包调用。为什么是这样的f(g($0))
和这样的{$0 + 2}
。后来自己推导了一番也就明白了。我觉得这中间推导的过程很有意思,有必要和大家分享一下。
1 | // 推导过程我就只写函数体,这样对比看起来要比较清晰。函数的调用部分也是一个匿名闭包这里就不讨论了。 |
写这篇文章的目的是我想用 Swift 这门语言以 Swift 的编程方式来思考问题。其实这里的推导过程也是从其他语言编程方式慢慢转向 Swift 语言编程方式的过程,而这个过程往往是最容易忽视的,希望我以后更加注重这个过程。恩,祝大家玩的开心!有问题下面可以留言,我会及时回复的 😁。