Pointfree.co Notes Ep.#23-25

这里记录了 pointfree.co 上, 以下三期视频的 notes:


zip的本质: 一个映射

最常见的 zip

考虑显然存在这样的映射A, B -> (A, B). 那么对任意一个包含T的结构Struct<T>, 我们一定可以给出映射g: Struct<A> , Struct<B> -> Struct<(A, B)>. 这样的映射gg就是zip.

这里的Struct可以是 Swift 中的许多有意义的结构, 比如:

  • Optional
  • Array
  • function
  • callback function
  • 任意有"包含"关系的 plain structure

例如, 针对Optional, 我们可以实现

1
2
3
4
func zip<A, B>(_ lhs: Optional<A>, _ rhs: Optional<B>) -> Optional<(A, B)> {
guard let a = lhs, let b = rhs else { return nil }
return .some((a, b))
}

又例如, 针对 function: () -> T, 我们可以实现

1
2
3
4
5
6
7
8
9
typealias Function<T> = () -> T
func zip<A, B>(
_ lhs: @escaping Function<A>,
_ rhs: @escaping Function<B>
) -> Function<(A, B)> {
return {
(lhs(), rhs())
}
}

Swift 提供的 zip

在标准库中zip(Sequence<A>, Sequence<B>) -> Sequence<(A, B)>就是一个通用的例子. (实为伪代码, 实际上 return type 是一个 concrete type)

第一方许多 framework 也提供了 zip. 例如: Combine 给许多遵循Publisher的 concrete type, 增加了成员方法, 结构为Publisher<A>.zip(Publisher<B>) -> Publisher<(A, B)>. (实为伪代码, 方法所在的类型和 return type 都是 concrete type, 泛型参数也稍有不同)

通用的二元 zip

给定一个映射f: (A, B) -> C, 对任意一个包含T的结构Struct<T>, 我们一定可以给出映射g: (Struct<A> , Struct<B>) -> Struct<C>.

例如, 针对 callback function: ((T) -> Void) -> Void的例子:

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
// 给定 f
func f<A, B, C>(_ lhs: A, _ rhs: B) -> C { fatalError() }

// 实现 g
func f<A, B, C>(_ lhs: A, _ rhs: B) -> C { fatalError() }
func zip<A, B, C>(
_ lhs: @escaping CallbackFunction<A>,
_ rhs: @escaping CallbackFunction<B>
) -> CallbackFunction<C> {
return { callback in
var lValue: A?
var rValue: B?
func check() {
guard let lValue = lValue, let rValue = rValue else { return }
callback(f(lValue, rValue))
}

lhs { returnValue in
lValue = returnValue
check()
}
rhs { returnValue in
rValue = returnValue
check()
}
}
}
  • 有时候称gg是 zip, 上文常见 zip 是这种定义下的一个特例, 即指定了f(A, B) = (A, B).
  • 有时候称f->g 是 zip, 这个 zip 更高阶, 并且可以由Struct的结构唯一确定.

例如, 高阶形式的Array的 zip 可以这么实现:

1
2
3
4
5
6
7
8
9
10
11
func zip<A, B, C>(with f: @escaping (A, B) -> C) -> ([A], [B]) -> [C] {
return { arrayA, arrayB in
var results: [C] = []
let maxCount = min(arrayA.count, arrayB.count)

for index in 0..<maxCount {
results.append(f(arrayA[index], arrayB[index]))
}
return results
}
}

多元 zip

在二元 zip 的基础上, 把 f 的输入个数增加即可. 最常见的多元 zip 也是基于 tuple的, 即f: (T1, T2, T3...) -> (T1, T2, T3...).
当然参照二元的例子, 也有高阶的多元 zip.

zipmap的联系

map可以看成是通用的多元 zip 的一个特例. 固定 f 中的输入个数为1个即可得到 map 的定义.
给定一个映射f: A -> B, 对任意一个包含T的结构Struct<T>, 我们一定可以给出映射g: Struct<A> -> Struct<B>.