Равномерны ли массивы? Могу ли я сравнить их, используя ==
До Swift 4.1 Array
не соответствовал Equatable
. Однако имелась перегрузка ==
, при которой два массива сравнивались с элементами Equatable
, что позволило компилировать это:
if ["1", "2"] == ["1", "2"] { // using <T : Equatable>(lhs: [T], rhs: [T]) -> Bool
print("true")
}
Однако в Swift 4.1 (доступно с Xcode 9.3) Array<Element>
теперь соответствует Equatable
, когда Element
соответствует Equatable
. Это изменение указано в журнале изменений :
Swift 4.1
[...]
- SE-0143 Стандартные типы библиотек
Optional
, Array
, ArraySlice
, ContiguousArray
и Dictionary
теперь соответствуют протоколу Equatable
, когда их типы элементов соответствуют Equatable
. Это позволяет оператору ==
составлять (например, можно сравнивать два значения типа [Int : [Int?]?]
с ==
), а также использовать различные алгоритмы, определенные для Equatable
типов элементов, такие как index(of:)
.
Ваш пример с multiDimArr.removeDups()
компилируется и запускается, как и ожидалось в 4.1, давая результат [[1, 2, 3], [1, 2, 4]]
.
В Swift 4.0.3 вы можете взломать его, добавив еще одну перегрузку removeDups()
для вложенных массивов:
extension Array {
func removeDups<T : Equatable>() -> [Element] where Element == [T] {
var result = [Element]()
for element in self{
if !result.contains(where: { element == $0 }) {
result.append(element)
}
}
return result
}
}
let multiDimArr = [[1, 2, 3], [1, 2, 3], [1, 2, 4]]
print(multiDimArr.removeDups()) // [[1, 2, 3], [1, 2, 4]]
К сожалению, это приводит к некоторому дублированию кода, но, по крайней мере, вы сможете избавиться от него при обновлении до 4.1.
Тот факт, что этот пример не компилируется ни в 4.0.3, ни в 4.1:
if [1, 2] == [1, 2] { // error: Ambiguous use of operator '=='
print("true")
}
связано с ошибкой SR-5944 - компилятор считает ее неоднозначной из-за перегрузок ==
для IndexSet
и IndexPath
(оба значения ExpressibleByArrayLiteral
). Но Swift по умолчанию должен использовать литерал массива Array
, что разрешает неоднозначность.
Сказать либо:
if [1, 2] as [Int] == [1, 2] {
print("true")
}
или не импортирование Foundation
решает проблему.
Наконец, стоит отметить, что производительность removeDups()
можно улучшить, если тип Element
также равен Hashable
, что позволяет ему работать в линейном, а не квадратичном времени:
extension Array where Element : Hashable {
func removeDups() -> [Element] {
var uniquedElements = Set<Element>()
return filter { uniquedElements.insert($0).inserted }
}
}
Здесь мы используем набор для хранения элементов, которые мы видели, исключая любые, которые мы уже вставили в него. Это также позволяет нам использовать filter(_:)
, , как @Alexander указывает .
И в Swift 4.2 , Array
также условно соответствует Hashable
, когда его Element
равен Hashable
:
Swift 4.2
[...]
- SE-0143 Стандартные типы библиотек
Optional
, Array
, ArraySlice
, ContiguousArray
, Dictionary
, DictionaryLiteral
, Range
и ClosedRange
теперь соответствуют к протоколу Hashable
, когда их элементы или связанные типы (в зависимости от обстоятельств) соответствуют Hashable
. Это делает синтезированные реализации Hashable
доступными для типов, которые включают хранимые свойства этих типов.