Как я могу расширить Swift's Array<T>
или T[]
печатать с помощью пользовательских функциональных утилит?
Просмотр API документов Swift показывает, что методы Array являются расширением T[]
, например:
extension T[] : ArrayType {
//...
init()
var count: Int { get }
var capacity: Int { get }
var isEmpty: Bool { get }
func copy() -> T[]
}
При копировании и вставке одного и того же источника и попытках любых вариантов, таких как:
extension T[] : ArrayType {
func foo(){}
}
extension T[] {
func foo(){}
}
Не удается построить с ошибкой:
Номинальный тип
T[]
не может быть продлен
Использование полного определения типа завершается неудачно с помощью Use of undefined type 'T'
:
extension Array<T> {
func foo(){}
}
И это также терпит неудачу с Array<T : Any>
и Array<String>
.
Любопытно, что Swift позволяет мне расширить нетипизированный массив с помощью:
extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}
Который это позволяет мне звонить с:
[1,2,3].each(println)
Но я не могу создать правильное расширение универсального типа, так как тип, похоже, теряется при прохождении через метод, например, пытаясь заменить встроенный фильтр Swift :
extension Array {
func find<T>(fn: (T) -> Bool) -> T[] {
var to = T[]()
for x in self {
let t = x as T
if fn(t) {
to += t
}
}
return to
}
}
Но компилятор обрабатывает его как нетипизированный, где он все еще позволяет вызывать расширение с помощью:
["A","B","C"].find { $0 > "A" }
И когда пошагово с отладчиком указывает тип, Swift.String
но это ошибка сборки, чтобы попытаться получить доступ к нему как String без приведения его к String
первому, то есть:
["A","B","C"].find { ($0 as String).compare("A") > 0 }
Кто-нибудь знает, как правильно создать типизированный метод расширения, который действует как встроенные расширения?
extension T[]
бит при щелчке Command по типу Array в XCode, но не видим способа реализовать его без ошибки.<T>
из сигнатуры метода.Ответы:
Для расширения типизированных массивов классами у меня работает следующее (Swift 2.2 ). Например, сортировка типизированного массива:
Попытка сделать это с помощью struct или typealias выдаст ошибку:
Обновить :
Чтобы расширить типизированные массивы не-классами, используйте следующий подход:
В Swift 3 некоторые типы были переименованы:
источник
[Iterator.Element]
?Через некоторое время, пробуя разные вещи, решение, кажется, удаляет
<T>
из подписи, например:Который теперь работает как задумано, без ошибок сборки:
источник
filter
функции:let x = ["A","B","C","X”].filter { $0.compare("A") > 0 }
filter
она функционально эквивалентна вашейfind
, то есть результат функции тот же. Если у вашего фильтра есть побочные эффекты, вам наверняка не понравятся результаты.filter
.filter
,map
иreduce
функции исходят из функции выполняются для их возвращаемых значений. Напротив,each
функция, которую вы определили выше, является примером функции, выполняемой для ее побочного эффекта, потому что она ничего не возвращает. Я думаю, мы можем согласиться с тем, что текущая реализация Swift не идеальна, и в документации ничего не говорится о ее характеристиках времени выполнения.Расширить все типы:
Расширить некоторые типы:
Расширить определенный тип:
источник
У меня была похожая проблема - я хотел расширить общий массив с помощью метода swap (), который должен был принимать аргумент того же типа, что и массив. Но как вы определяете универсальный тип? Я обнаружил методом проб и ошибок, что ниже работает:
Ключом к этому было слово «Элемент». Обратите внимание, что я нигде не определял этот тип, он, кажется, автоматически существует в контексте расширения массива и ссылается на тип элементов массива.
Я не уверен на 100%, что там происходит, но думаю, что это, вероятно, потому, что «Элемент» - это связанный тип массива (см. «Связанные типы» здесь https://developer.apple.com/library/ios/documentation /Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189 )
Однако я не вижу никакой ссылки на это в описании структуры Array ( https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift / struct / s: Sa ) ... так что я все еще немного неуверен.
источник
Array
является универсальным типом:Array<Element>
(см. swiftdoc.org/v2.1/type/Array ),Element
является заполнителем для содержащегося типа. Например:var myArray = [Foo]()
означает, чтоmyArray
будет содержать только типFoo
.Foo
в этом случае "отображается" на родовой заполнительElement
. Если вы хотите изменить общее поведение массива (через расширение), вы должны использовать общий заполнитель,Element
а не какой-либо конкретный тип (например, Foo).Использование Swift 2.2 : я столкнулся с подобной проблемой при попытке удалить дубликаты из массива строк. Мне удалось добавить расширение для класса Array, которое делает именно то, что я искал.
Добавление этих двух методов в класс Array позволяет мне вызывать один из двух методов в массиве и успешно удалять дубликаты. Обратите внимание, что элементы в массиве должны соответствовать протоколу Hashable. Теперь я могу сделать это:
источник
let deDuped = Set(dupes)
, который вы можете вернуть в неразрушающем методе, которыйtoSet
Если вы хотите узнать о расширении Arrays и других типах кода проверки классов в этом репозитории github https://github.com/ankurp/Cent
Начиная с Xcode 6.1 синтаксис для расширения массивов выглядит следующим образом
источник
Я взглянул на заголовки стандартной библиотеки Swift 2, и вот прототип для функции фильтра, которая делает совершенно очевидным, как создавать свои собственные.
Это не расширение для Array, а для CollectionType, поэтому тот же метод применяется к другим типам коллекций. @noescape означает, что переданный блок не покинет область действия функции фильтра, что обеспечивает некоторую оптимизацию. Я с большой буквы S - это класс, который мы расширяем. Self.Generator - это итератор, который выполняет итерацию по объектам в коллекции, а Self.Generator.Element - это тип объектов, например, для массива [Int?] Self.Generator.Element будет Int ?.
В общем, этот метод фильтра может быть применен к любому CollectionType, ему нужен блок фильтра, который берет элемент коллекции и возвращает Bool, а также возвращает массив исходного типа. Итак, объединяя это, вот метод, который я считаю полезным: он объединяет карту и фильтр, беря блок, который отображает элемент коллекции в необязательное значение, и возвращает массив тех необязательных значений, которые не равны nil.
источник
источник
( Swift 2.x )
Вы также можете расширить массив, чтобы он соответствовал протоколу, содержащему blue-rpints для методов универсального типа, например, протокол, содержащий ваши пользовательские функциональные утилиты для всех элементов универсального массива, соответствующих некоторому ограничению типа, скажем, протоколу
MyTypes
. Преимущество этого подхода в том, что вы можете писать функции, принимающие универсальные аргументы массива, с ограничением, что эти аргументы массива должны соответствовать вашему протоколу утилит пользовательских функций, скажем, протоколуMyFunctionalUtils
.Вы можете получить это поведение либо неявно, ограничив тип элементов массива
MyTypes
, либо - как я покажу в методе, который я опишу ниже, - довольно аккуратно, явно, позволяя заголовку общих функций массива напрямую показывать, что входные массивы соответствуетMyFunctionalUtils
.Мы начнем с протоколов
MyTypes
для использования в качестве ограничения типа; расширять типы, которые вы хотите вписать в ваши дженерики с помощью этого протокола (пример ниже расширяет основные типыInt
иDouble
, а также пользовательский типMyCustomType
)Протокол
MyFunctionalUtils
(содержит чертежи наших дополнительных утилит функций универсального массива), а затем, расширение массиваMyFunctionalUtils
; реализация метода (-ов) с синей печатью:Наконец, тесты и два примера, показывающие функцию, принимающую универсальные массивы, в следующих случаях, соответственно
Показано неявное утверждение о том, что параметры массива соответствуют протоколу «MyFunctionalUtils», посредством ограничения типа элементов массива на «MyTypes» (функция
bar1
).Явно показывает, что параметры массива соответствуют протоколу «MyFunctionalUtils» (функция
bar2
).Тест и примеры следующие:
источник
источник
$0 as! Double
) борются против системы типов Swift, а также, на мой взгляд, побеждают цель вопроса OP. Делая это, вы теряете любой потенциал для оптимизации компилятора для вычислений, которые вы на самом деле хотите сделать, а также загрязняете пространство имен Array бессмысленными функциями (зачем вам хотеть видеть .calculateMedian () в массиве UIViews, или что-нибудь, кроме Double в этом отношении?). Существует лучший способ.extension CollectionType where Generator.Element == Double {}