Swift 4 新功能 -(二)
- 协议相关类型的约束
- 字典(Dictionary)和集合(Set)的增强
- MutableCollection.swapAt 方法
- reduce 和 inout
- 泛型下标
- NSNumber 桥接
- 类和协议的组合
协议相关类型的约束
SE-0142: 协议的相关类型可以用where
语句约束. 看似一小步,却是类型系统表达能力的一大步,让标准库可以大幅简化. 喜大普奔的是, Sequence
和 Collection
在Swift 4中用上这个就更直观了.
- Sequence.Element
Sequence
现在有了自己的相关类型Element
. 原先Swift 3中到处露脸的Iterator.Element
, 现在瘦身成Element
:
extension Sequence where Element: Numeric {
var sum: Element {
var result: Element = 0
for item in self {
sum += item
}
return result
}
}
[1,2,3,4].sum
- 当扩展 Sequence 和 Collection 时所需约束更少
// 在Swift 3时代, 这种扩展需要很多的约束:
//extension Collection where Iterator.Element: Equatable,
// SubSequence: Sequence,
// SubSequence.Iterator.Element == Iterator.Element
//
// 而在Swift 4, 编译器已经提前知道了上述3个约束中的2个, 因为可以用相关类型的where语句来表达它们.
extension Collection where Element: Equatable {
func prefieIsEqualSuffix(_ n: Int) -> Bool {
let head = prefix(n)
let suff = suffix(n).reversed()
return head.elementsEqual(suff)
}
}
[1,2,3,4,2,1]. prefieIsEqualSuffix(2)
字典(Dictionary) 和 集合(Set) 的增强
SE-0165 加了一些很nice的 Dictionary
和 Set
增强.
-
基于序列(Sequence)的构造器
从一个键值对序列构造字典.
let hotLanguage = ["Swift", "Python", "Kotlin","JAVA","C++"]
let hotLanguageRanking = Dictionary(uniqueKeysWithValues: zip(1..., hotLanguage))
hotLanguageRanking[2]
- 合并(merge)构造器 & merge 方法
当从一个序列构造字典,或把一个序列合并到字典中,描述如何处理重复的键.
let duplicates = [("a", 1), ("b", 2), ("a", 3), ("b", 4)]
let letters = Dictionary(duplicates, uniquingKeysWith: { (first, _) in first })
>>> letters = ["b": 2, "a": 1]
合并构造器或 merge
方法遇到一个字典时就没那么舒服了. 因为字典的元素类型是一个 带标签的 元组型(key: Key, value: Value)
但上述2个方法却要求一个 无标签的 元组型(Key, Value)
, 不得已要手工转换. 希望这个今后能完善. 见 SR-922 和 SR-4969.
let defaults = ["foo": false, "bar": false, "baz": false]
var options = ["foo": true, "bar": false]
// 会产生一个烦人的类型转换警告
// error: error: generic parameter 'S' could not be inferred
options.merge(defaults) { (old, _) in old }
// 替代方法
options.merge(defaults.map { $0 }) { (old, _) in old }
- 下标的默认值
你现在可以给下标中加一个默认值参数, 当key不存在时会返回这个值, 这样便可让返回类型非Optional.
hotLanguage[4, default: "(unknown)"]
在你想通过下标更新一个值时,这个功能就非常有用:
let source = "how now brown cow"
var frequencies: [Character: Int] = [:]
for c in source {
frequencies[c, default: 0] += 1
}
frequencies
var words = """
天姥连天向天横 势拔五岳掩赤城
天台四万八千丈 对此欲倒东南倾
我欲因之梦吴越 一夜飞度镜湖月
湖月照我影 送我至剡溪
"""
var frequencies: [Character: Int] = [:]
for word in words.components(separatedBy: .whitespacesAndNewlines).joined() {
frequencies[word, default: 0] += 1
}
for (word , count) in frequencies {
if count > 1 {
print(word, count)
}
}
- Dictionary相关的 map 和 filter
filter
返回一个 Dictionary
而非 Array
. 相似的, 新方法mapValues
转换值的同时保持字典结构.
let filtered = hotLanguageRaking.filter {
$0.key % 2 == 0
}
type(of: filtered)
let mapped = hotLanguageRanking.mapValues { value in
value.uppercased()
}
mapped
- Set.filter 现在同样返回一个
Set
而不是Array
let set: Set = [1,2,3,4,5]
let filteredSet = set.filter { $0 % 2 == 0 }
type(of: filteredSet)
- 分组一个序列
把一个序列分成几组, 比如联系人按姓分组.
let contacts = ["Julia", "Susan", "John", "Alice", "Alex"]
let grouped = Dictionary(grouping: contacts, by: { $0.first! })
>>> grouped = ["J": ["Julia", "John"], "S": ["Susan"], "A": ["Alice", "Alex"]]
MutableCollection.swapAt 方法
SE-0173 介绍了一种交换一个集合中两个元素的新方法. 与既有的 swap(_:_:)
方法不同, swapAt(_:_:)
接受一个要交换的元素切片, 而不是整个元素本身 (通过 inout
参数).
加这个的目的是 swap
方法带2个inout
参数 不再兼容新的独占式内存访问规则,见SE-0176. 既有的 swap(_:_:)
方法不能再交换同一个集合中的两个元素.
var numbers = [1,2,3,4,5]
numbers.swapAt(0,1)
// Swift 4中非法
//swap(&numbers[3], &numbers[4])
//numbers
reduce 和 inout
SE-0171 新增reduce
的一个变体, 让部分结果以inout
传递给组合函数. 如此一来可以通过消除中间结果的副本来递增一个序列,大幅提升reduce
算法的性能.
SE-0171 未实现.
// 尚未实现
//extension Sequence where Element: Equatable {
// func uniq() -> [Element] {
// return reduce(into: []) { (result: inout [Element], element) in
// if result.last != element {
// result.append(element)
// }
// }
// }
//}
//[1,1,1,2,3,3,4].uniq()
英文 By Ole Begemann 中文 by 小波
相关视频