Home Swift Tips (1) - Function
Post
Cancel

Swift Tips (1) - Function

函数的柯里化

Swift 里可以将方法进行柯里化 (Currying),这是也就是把接受多个参数的方法进行一些变形,使其更加灵活的方法。

在非函数式的代码时,我们可能会这样写一个方法:

1
2
3
func isNumber(_ base: Int, greaterThan target: Int) -> Bool {
	return base > target
}

同样的功能如果使用柯里化的函数:

1
2
3
4
5
6
7
8
func greaterThan(comparer: Int) -> Int -> Bool {
    return { $0 > comparer }
}

let greaterThan10 = greaterThan(10);

greaterThan10(13)    // => true
greaterThan10(9)     // => false

柯里化是一种量产相似方法的好办法。

使用函数当做对象

我们可以结合函数实现诸多简便的功能,在 Swift 中,函数也是一等公民,利用函数本身的特点,可以更高效的组合出想要的功能。

举例:我们可以给Array<Element>添加方法,获取一个contains(Element) -> Bool的类型的函数输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
extension Array where Element: Equatable {
    
    public static func contains<T: Equatable>(in array: [T]) -> (T) -> Bool {
        return { obj in
            return (array.filter { $0 == obj }).count > 0
        }
    }
    
    public static func notContains<T: Equatable>(in array: [T]) -> (T) -> Bool {
        return { obj in
            return !contains(in: array)(obj)
        }
    }
    
    public func intersection(with other: [Element]) -> [Element] {
        return self.filter(Array.contains(in: other))
    }
    
    public func union(with other: [Element]) -> [Element] {
        return self + other.minus(with: self)
    }
    
    public func minus(with other: [Element]) -> [Element] {
        return self.filter(Array.notContains(in: other))
    }
}

let contains = Array.contains(in: [1, 2, 3])
contains(1)  ==>  true
contains(4)  ==>  false

[1, 2, 3].intersection(with: [2, 3])  ==>  [2, 3]
[1, 2, 3].minus(with: [2, 3])  ==>  [1]
[1, 2, 3].union(with: [3, 4])  ==>  [1, 2, 3, 4]

对于任何实现了Equatable的对象集合,都可以使用这些方法做运算。

Tuple 元组

Tuple 是 Swift 带给我们的新功能,在普通场景下使用的时候,主要有两点功能:

  • 标明含义:自己或其他小伙伴在接收到这个参数时,很容易根据参数标识来分辨元组内的属性含义。
1
2
3
4
5
switch some {
	case let x:
		x.up = ...
		x.down = ...
}
  • 函数的多结果输出:当一个函数想输出多个数据,而数据之间毫无关联,不能结合成一个对象;或信息太少不至于用对象代替时,我们可以使用多元组当做函数的输出。这点跟Go语言的函数多返回值思想很像。
1
func requestCargoInfo(from url: URL) -> (name: String, destination: String) { ... }

@autoclosure

@autoclosure 可以说是 Apple 的一个非常神奇的创造,因为这更多地是像在 “hack” 这门语言。简单说,@autoclosure 做的事情就是把一句表达式自动地封装成一个闭包 (closure)。这样有时候在语法上看起来就会非常漂亮。

比如系统的 precondition 函数,如果我们自己写一个相似功能的:

1
2
3
func precondition(_ condition: () -> Bool, _ message: String ...) {...}

precondition({ return 2 > 1 }, "true")

但是使用@autoclosure之后,如系统的:

1
2
3
4
5
public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, 
	file: StaticString = #file, line: UInt = #line)

// 我们就可以更简洁的使用:
precondition(2 > 1, "true")

另一种使用方式,在 Vapor 源码中,为了避免过度使用资源、耗费性能,通常是确定某个条件之后再使用某个属性。比如这段代码:

1
2
3
4
5
6
7
8
public func unwrap(or error: @autoclosure @escaping () -> Error) -> Future<Expectation.WrappedType> {
    return map(to: Expectation.WrappedType.self) { optional in
        guard let wrapped = optional.wrapped else {
            throw error()
        }
        return wrapped
	}
}

这里 Error 是在 optional 确定不为 nil 之后再生成的。最后要提一句的是,@autoclosure 并不支持带有输入参数的写法,也就是说只有形如 () -> T 的参数才能使用这个特性进行简化。


This post is licensed under CC BY 4.0 by the author.