Как расширить функциональность типизированных массивов (Array<T>) в Swift?
Использование расширений с where-ограничениями (правильный путь)
В Swift вы можете расширять Array (или Collection) с помощью where-ограничений для конкретных типов. Это позволяет добавлять методы только к массивам, элементы которых соответствуют определённому протоколу или типу.
Вот как это работает для вашего примера с [Int]:
extension Array where Element == Int {
func sumOfSquares() -> Int {
return self.reduce(0) { $0 + $1 * $1 }
}
}
// Использование:
let numbers = [1, 2, 3, 4]
print(numbers.sumOfSquares()) // Выведет: 30
Для массивов строк с удалением дубликатов:
extension Array where Element == String {
func unique() -> [String] {
var seen = Set<String>()
return self.filter { seen.insert($0).inserted }
}
}
let words = ["a", "b", "a", "c", "b"]
print(words.unique()) // ["a", "b", "c"]
Расширение через протоколы (более гибкий подход)
Если вы хотите, чтобы метод работал для всех типов, поддерживающих определённое поведение (например, числовые типы), используйте протоколы:
protocol NumericSquarable {
static func *(lhs: Self, rhs: Self) -> Self
static func +(lhs: Self, rhs: Self) -> Self
init(_ value: Int)
}
extension Int: NumericSquarable {}
extension Double: NumericSquarable {}
extension Float: NumericSquarable {}
extension Array where Element: NumericSquarable {
func sumOfSquares() -> Element {
return self.reduce(Element(0)) { $0 + $1 * $1 }
}
}
// Работает и для Int, и для Double:
print([1, 2, 3].sumOfSquares()) // 14
print([1.5, 2.5].sumOfSquares()) // 8.5
Важные замечания
- Производительность: Расширения с where-ограничениями не влияют на производительность, так как компилятор генерирует код только для указанных типов.
- Ограничения: Вы не можете переопределить существующие методы Array через extension, только добавить новые.
- Альтернатива: Если вам нужно добавить метод только для одного конкретного массива, рассмотрите
extension Array where Element == MyCustomType.
Этот подход широко используется в стандартной библиотеке Swift. Например, метод sort() для массивов с элементами, соответствующими Comparable, реализован именно так.
Расширение через глобальные функции (простой, но менее элегантный способ)
Если вы не хотите использовать where-ограничения или работаете с очень специфичными случаями, можно написать глобальные функции, принимающие массив как параметр:
func sumOfSquares(for numbers: [Int]) -> Int {
return numbers.reduce(0) { $0 + $1 * $1 }
}
func uniqueStrings(from array: [String]) -> [String] {
var seen = Set<String>()
return array.filter { seen.insert($0).inserted }
}
// Использование:
let result1 = sumOfSquares(for: [1, 2, 3]) // 14
let result2 = uniqueStrings(from: ["a", "b", "a"]) // ["a", "b"]
Этот подход проще для понимания, но теряет преимущества точечного синтаксиса (dot notation). Он полезен, когда расширение невозможно (например, для кортежей) или когда вы хотите явно указать тип массива в имени функции.
Когда использовать:
- Для быстрых прототипов или скриптов.
- Когда функция должна работать с несколькими типами массивов, но не подходит протокольное решение.
- Для избежания конфликтов имён, если два расширения имеют одинаковые методы.