Swift 4.0.2中多维数组去重及数组Equatable合规性问题
咱们结合你使用的Swift 4.0.2版本,逐个拆解你遇到的三个问题:
1. 数组是否符合Equatable协议?能否使用==进行比较?
在Swift 4.0.2里,数组本身并没有直接遵守Equatable协议,但有个特殊情况:苹果给部分特定类型的数组(比如[String])单独实现了==运算符,所以这类数组可以直接用==比较。
而泛型场景下的条件一致性(也就是当数组元素Element遵守Equatable时,数组[Element]自动获得Equatable支持),是Swift 4.1才正式引入的特性。所以在你的4.0.2版本里,不是所有数组都能使用==,只有少数系统提前适配的数组类型可以,其他类型(比如[Int])不行,除非你自己实现比较逻辑。
2. 为何字符串数组与Int数组的比较表现存在差异?
这完全是Swift 4.0.2的版本局限性导致的:
- 对于
[String],标准库单独实现了==运算符,所以你能直接比较两个字符串数组是否相等。 - 对于
[Int]这类基础类型数组,4.0.2既没有泛型条件一致性支持,也没有单独为[Int]实现==,同时编译器可能因为存在其他全局==实现而产生歧义,所以会抛出“Ambiguous use of operator '=='”的错误。
这个问题在Swift 4.1及以上版本就被彻底修复了,因为新版本引入了完整的条件一致性,只要数组元素遵守Equatable,数组就自动支持==比较。
3. 如何实现多维数组的去重?
你的现有扩展要求Element : Equatable,但在4.0.2里,[Int]并不遵守Equatable(缺少条件一致性),所以[[Int]]调用removeDups()会报错。这里给你两个可行的解决方案:
方案1:手动添加数组的条件Equatable实现(适配4.0.2)
我们可以自己模拟Swift 4.1+的条件一致性行为,给数组添加一个扩展,让元素为Equatable的数组也遵守Equatable:
extension Array : Equatable where Element : Equatable { public static func == (lhs: Array, rhs: Array) -> Bool { guard lhs.count == rhs.count else { return false } for (index, element) in lhs.enumerated() { if element != rhs[index] { return false } } return true } }
添加这个扩展后,[Int]就会自动遵守Equatable,你的原有removeDups()方法就能直接用于多维数组了:
let multiDimArr : [[Int]] = [[1,2,3], [1,2,3], [1,2 ,4]] multiDimArr.removeDups() // 返回 [[1,2,3], [1,2,4]],正常工作
方案2:修改去重扩展,支持自定义相等判断(更通用)
如果不想依赖Equatable,可以把去重扩展改成接受一个自定义的相等判断闭包,这样不管元素是否遵守Equatable,都能实现去重:
extension Array { func removeDups(by areEqual: (Element, Element) -> Bool) -> [Element] { var result = [Element]() for element in self { if !result.contains(where: { areEqual($0, element) }) { result.append(element) } } return result } }
使用时,手动传入多维数组的比较逻辑:
let multiDimArr : [[Int]] = [[1,2,3], [1,2,3], [1,2 ,4]] // 如果你已经加了方案1的Equatable扩展,可以直接用$0 == $1 let uniqueArr = multiDimArr.removeDups { $0 == $1 } // 或者直接写完整的比较逻辑,不需要依赖Equatable let uniqueArr = multiDimArr.removeDups { lhs, rhs in guard lhs.count == rhs.count else { return false } for (lElement, rElement) in zip(lhs, rhs) { if lElement != rElement { return false } } return true }
当然,如果你能升级到Swift 4.1+版本,一切会更简单——系统已经原生支持数组的条件一致性,[Int]会自动遵守Equatable,你的原有removeDups()扩展直接就能处理多维数组,不需要任何额外修改。
内容的提问来源于stack exchange,提问作者mfaani

